diff --git a/.astylerc b/.astylerc deleted file mode 100644 index 653754e724..0000000000 --- a/.astylerc +++ /dev/null @@ -1,87 +0,0 @@ -# -# MetaCall Artistic Style Configuration by Parra Studios -# A configuration for Artistic Style linter. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -# Bracket Style Options -# - ---style=allman # Use Allman style with broken brackets - -# -# Tabs Options -# - ---indent=force-tab=4 # Use tab for indentation with size equal to 4 - -# -# Bracket Modify Options -# - ---attach-namespaces # Attach brackets to bracketed namespace statement ---attach-extern-c # Attach brackets to bracketed extern "C" statement - -# -# Indentation Options -# - ---indent-classes # Indent class and struct blocks ---indent-modifiers # Indent class and struct access modifiers ---indent-switches # Indent switch blocks so that the case statements are indented in the block ---indent-labels # Add extra indentation to labels ---indent-preproc-define # Indent multi-line preprocessor definitions ---indent-preproc-cond # Indent preprocessor conditional statements to the same level as source code ---indent-col1-comments # Indent C++ comments beginning in column one ---min-conditional-indent=0 # Minimal indent when a header is built of multiple lines ---max-instatement-indent=80 # Maximum number of spaces to indent a continuation line - -# -# Padding options -# - ---break-blocks # Pad empty lines around header blocks ---pad-oper # Insert space padding around operators ---pad-header # Insert space padding between a header and the following paren ---unpad-paren # Remove extra space padding around parenthesis on the inside and outside ---align-pointer=middle # Align pointer to the middle ---align-reference=middle # Align reference to the middle - -# -# Formatting options -# - ---add-brackets # Add brackets to unbracketed one line conditional statements ---max-code-length=120 # Define max code lenght limit ---break-after-logical # Add line break after logical operators in a conditional statement - -# -# Indentation mode -# - ---mode=c # Use C/C++ indentation mode - -# -# Other options -# - ---dry-run # Do not create any file nor overwrite -#--sufix=none # Overwrite original file ---recursive # Process all directories recursively ---preserve-date # Preserve original file date ---verbose # Display optional information and staticial data ---lineend=windows # Use windows line endings diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..bffce04a3e --- /dev/null +++ b/.clang-format @@ -0,0 +1,110 @@ +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +# AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: true +# AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true +AlignEscapedNewlines: DontAlign +# AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +# AllowShortBlocksOnASingleLine: false +# AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +# AllowShortIfStatementsOnASingleLine: false +# AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: None +# AlwaysBreakAfterReturnType: None +# AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +# BinPackArguments: true +# BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +# BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +# BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +# BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +# BreakStringLiterals: true +ColumnLimit: 0 +# CommentPragmas: '^ IWYU pragma:' +# CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +# DerivePointerAlignment: false +# DisableFormat: false +# ExperimentalAutoDetectBinPacking: false +# FixNamespaceComments: true +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IncludeBlocks: Preserve +IncludeCategories: + - Regex: '".*"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 +# IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentExternBlock: NoIndent +IndentPPDirectives: BeforeHash +IndentWidth: 4 +# IndentWrappedFunctionNames: false +# JavaScriptQuotes: Leave +# JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +# MacroBlockBegin: '' +# MacroBlockEnd: '' +# MaxEmptyLinesToKeep: 1 +# NamespaceIndentation: None +# PenaltyBreakAssignment: 2 +# PenaltyBreakBeforeFirstCallParameter: 19 +# PenaltyBreakComment: 300 +# PenaltyBreakFirstLessLess: 120 +# PenaltyBreakString: 1000 +# PenaltyExcessCharacter: 1000000 +# PenaltyReturnTypeOnItsOwnLine: 60 +# PointerAlignment: Right +# RawStringFormats: +# - Delimiter: pb +# Language: TextProto +# BasedOnStyle: google +# ReflowComments: true +# SortIncludes: true +# SortUsingDeclarations: true +# SpaceAfterCStyleCast: false +# SpaceAfterTemplateKeyword: true +# SpaceBeforeAssignmentOperators: true +# SpaceBeforeParens: ControlStatements +# SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +# SpacesInAngles: false +# SpacesInContainerLiterals: true +# SpacesInCStyleCastParentheses: false +# SpacesInParentheses: false +# SpacesInSquareBrackets: false +TabWidth: 4 +UseCRLF: true +UseTab: Always diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 640b3285ed..0000000000 --- a/.editorconfig +++ /dev/null @@ -1,31 +0,0 @@ -# -# MetaCall EditorConfig Configuration by Parra Studios -# EditorConfig helps developers define and maintain consistent coding -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# styles between different editors and IDEs (http://editorconfig.org). -# - -root = true - -[*] -indent_style = tab -indent_size = tab -tab_width = 4 -end_of_line = crlf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -max_line_length = 120 diff --git a/.env b/.env index 5cd3584fab..cf3ae699c0 100644 --- a/.env +++ b/.env @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,6 +22,5 @@ COMPOSE_PROJECT_NAME='metacall' # Configure default variables METACALL_PATH=/usr/local/metacall -METACALL_NODE_BUILD_TYPE=Release -METACALL_CORE_BUILD_TYPE=release -METACALL_BASE_IMAGE=debian:stretch-slim +METACALL_BUILD_TYPE=relwithdebinfo +METACALL_BASE_IMAGE=debian:trixie-slim # debian:bookworm-slim # ubuntu:noble # ubuntu:jammy # alpine:3.17 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e13dab85d9..ea802c726b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,9 +1,19 @@ # Contributing When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. +email, or any other method with the owners of this repository before making a change. Here is an examplary process you can follow to create an issue on MetaCall: -Please note we have a [Code of Conduct](/.github/CODE_OF_CONDUCT), please follow it in all your interactions with the project. +1. Identify a problem or a possible addition to Metacall codebase/operation
+for instance: ```pre-commit-clang-format not working on windows...``` +2. Then go to the necessary MetaCall gitHub repository and click on the "Issues" tab at the top of the page. Indicate your issue's interest by tagging it as a bug report, custom issue, documentation, feature, or discussion.
+in this case: ```metacall/core``` +3. Type in a title and description for your issue. Be as detailed as possible, including any error messages or steps to reproduce the issue. Image example:
+![issue image](https://user-images.githubusercontent.com/93955843/220493308-0ce3f101-6957-43fb-96d1-7a730ccf304c.PNG) + +4. You can assign the issue to yourself, add labels or a milestone to it, and attach files if necessary. +5. Submit the newly created issue and start working on a solution by creating a fork of the repository.
(Here to issue used for explanation) + +Please note we have a [Code of Conduct](/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. ## Pull Request Process diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..1be1f7c86b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: 🐛 Bug Report +about: Submit a bug report to help us improve +labels: "bug" +assignees: '' +--- + +## 🐛 Bug Report + + + +### Expected Behavior + + +### Current Behavior + + +### Possible Solution + + +### Steps to Reproduce + + +1. +2. +3. +4. + +### Context (Environment) + + + + + +### Detailed Description + + +### Possible Implementation + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..41f18c3c10 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +blank_issues_enabled: false +contact_links: + - name: Discussions + url: https://github.com/metacall/core/discussions + about: This is where long discussions could take place outside of the chat platforms. + + diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000..841a03f189 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,23 @@ +--- +name: ➕ Custom Issue +about: Create a custom issue +labels: "custom" +assignees: '' +--- + +Hey there and thank you for using Github Issues! + +Our project, as you've probably heard, is getting really popular and truth is we're getting a bit overwhelmed by the activity surrounding it. There are just too many issues for us to manage properly. + +Do the checklist before filing an issue: + +- [ ] Is this something you can **debug and fix**? Create an apt issue and send us a pull request! Bug fixes and documentation fixes are welcome. +- [ ] Have an usage question? Ask it on [Discussions](https://github.com/metacall/core/discussions/new) or community chats. We use Github Discussions for usage questions and GitHub Issues for bugs. +- [ ] Have an idea for a feature? Use the feature issue and propose your ideas. + +None of the above, create a custom issue + +Make sure to add **all the information needed to help us understand what you wish to convey** so that someone can help. + +------------------------------------------------------------------ + diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000000..f96efa63f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,10 @@ +--- +name: 📚 Documentation +about: Report an issue related to documentation +labels: "documentation" +assignees: '' +--- + +## 📚 Documentation + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..13fcc6e8f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: 🚀 Feature +about: Submit a proposal for a new feature +labels: "feature" +assignees: '' +--- + +## 🚀 Feature + + + +### Is your feature request related to a problem? + + +### Describe the solution you'd like + + +### Describe alternatives you've considered + + +### Additional context + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e69de29bb2..7b3e570695 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +# Description + +Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change. + +Fixes #(issue_no) + + + +## Type of change + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update +- [ ] Documentation update + +# Checklist: + +- [ ] I have performed a self-review of my own code. +- [ ] I have commented my code, particularly in hard-to-understand areas. +- [ ] I have made corresponding changes to the documentation. +- [ ] My changes generate no new warnings. +- [ ] I have added tests/screenshots (if any) that prove my fix is effective or that my feature works. +- [ ] I have tested the tests implicated (if any) by my own code and they pass (`make test` or `ctest -VV -R `). +- [ ] If my change is significant or breaking, I have passed all tests with `./docker-compose.sh test &> output` and attached the output. +- [ ] I have tested my code with `OPTION_BUILD_ADDRESS_SANITIZER` or `./docker-compose.sh test-address-sanitizer &> output` and `OPTION_TEST_MEMORYCHECK`. +- [ ] I have tested my code with `OPTION_BUILD_THREAD_SANITIZER` or `./docker-compose.sh test-thread-sanitizer &> output`. +- [ ] I have tested with `Helgrind` in case my code works with threading. +- [ ] I have run `make clang-format` in order to format my code and my code follows the style guidelines. + +If you are unclear about any of the above checks, have a look at our documentation [here](https://github.com/metacall/core/blob/develop/docs/README.md#63-debugging). diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000000..67a684cd07 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,202 @@ +name: Benchmarks + +on: + pull_request: + push: + tags: + - "v*.*.*" + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + benchmark-unix: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest] + + env: + LTTNG_UST_REGISTER_TIMEOUT: 0 + NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Prepare MacOS Installation + if: matrix.os == 'macos-latest' + run: | + echo "Uninstall CMake" + brew uninstall --force cmake + + echo "Uninstall NodeJS and NPM" + npm uninstall npm -g + rm -rf /usr/local/lib/node_modules/npm + + echo "Uninstall Ruby" + brew uninstall --force --ignore-dependencies ruby + brew cleanup -s ruby + brew cleanup --prune-prefix + RUBY_FRAMEWORK_DIR=$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/Ruby.framework + sudo rm -rf $RUBY_FRAMEWORK_DIR + + echo "Uninstall Go" + brew uninstall --force go + brew autoremove + sudo rm -rf /usr/local/Cellar/go + sudo rm -rf /usr/local/go + sudo rm -rf /usr/local/opt/go + sudo rm -rf /etc/paths.d/go + sudo rm -rf /usr/local/bin/go + sudo rm -rf /usr/local/bin/gofmt + + echo "Uninstall Java" + sudo rm -rf /Library/Java/JavaVirtualMachines/* + sudo rm -rf /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin + sudo rm -rf /Library/PreferencePanes/JavaControlPanel.prefPane + unset JAVA_HOME + + echo "Export XCode SDK Root" + echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV + + # TODO: Add support for NetCore Bench + - name: Set up the environment + run: | + sh ./tools/metacall-environment.sh $METACALL_INSTALL_OPTIONS + env: + METACALL_INSTALL_OPTIONS: base python nodejs ruby # TODO: Implement NetCore once it works with MacOS + + - name: Configure + run: | + mkdir -p build + cd build + if [ "$(uname)" == "Darwin" ]; then + . .env + fi + bash ../tools/metacall-configure.sh $METACALL_CONFIGURE_OPTIONS + env: + METACALL_CONFIGURE_OPTIONS: release scripts python nodejs ruby benchmarks # TODO: Implement NetCore once it works with MacOS + + - name: Build + working-directory: ./build + run: | + if [ "$(uname)" == "Darwin" ]; then + . .env + fi + bash ../tools/metacall-build.sh $METACALL_BUILD_OPTIONS + env: + METACALL_BUILD_OPTIONS: release benchmarks + + - name: Merge benchmarks + if: ${{ github.event_name != 'pull_request' }} + run: python3 ./tools/metacall-benchmarks-merge.py ./build/benchmarks + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + if: ${{ github.event_name != 'pull_request' }} + with: + name: MetaCall Benchmark (${{ matrix.os }}) + tool: 'googlecpp' + output-file-path: ./build/benchmarks/metacall-benchmarks.json + # Access token to deploy GitHub Pages branch + github-token: ${{ secrets.BENCHMARKS_PUSH_TOKEN }} + # Disable push and deploy GitHub pages branch automatically + auto-push: false + # Github Pages repository name + gh-repository: github.com/metacall/core-benchmarks + # Github Pages branch name + gh-pages-branch: main + # Output directory + benchmark-data-dir-path: ./${{ matrix.os }} + + - name: Push benchmark result + if: ${{ github.event_name != 'pull_request' }} + run: | + cd benchmark-data-repository + git push https://$REPO_KEY@github.com/metacall/core-benchmarks.git + env: + REPO_KEY: ${{secrets.BENCHMARKS_PUSH_TOKEN}} + + benchmark-windows: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-2022, windows-2025] + + env: + LTTNG_UST_REGISTER_TIMEOUT: 0 + NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Activate the Visual Studio Developer Prompt + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Set up the environment + run: cmd.exe /c "powershell .\tools\metacall-environment.ps1 $Env:METACALL_INSTALL_OPTIONS" + env: + METACALL_INSTALL_OPTIONS: python nodejs ruby # TODO: Implement NetCore once it works with Windows + + # TODO: Use release when this bug is solved: https://github.com/metacall/core/issues/461 + - name: Configure + run: | + $METACALL_PATH = $PWD + md -Force "$METACALL_PATH\build" + cd "$METACALL_PATH\build" + cmd.exe /c "powershell ..\tools\metacall-configure.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: debug scripts python nodejs ruby benchmarks # TODO: Implement NetCore once it works with Windows + + # TODO: Use release when this bug is solved: https://github.com/metacall/core/issues/461 + - name: Build + working-directory: ./build + run: cmd.exe /c "powershell ..\tools\metacall-build.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: debug benchmarks + + - name: Merge benchmarks + if: ${{ github.event_name != 'pull_request' }} + run: python3 ./tools/metacall-benchmarks-merge.py ./build/benchmarks + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + if: ${{ github.event_name != 'pull_request' }} + with: + name: MetaCall Benchmark (${{ matrix.os }}) + tool: 'googlecpp' + output-file-path: ./build/benchmarks/metacall-benchmarks.json + # Access token to deploy GitHub Pages branch + github-token: ${{ secrets.BENCHMARKS_PUSH_TOKEN }} + # Disable push and deploy GitHub pages branch automatically + auto-push: false + # Github Pages repository name + gh-repository: github.com/metacall/core-benchmarks + # Github Pages branch name + gh-pages-branch: main + # Output directory + benchmark-data-dir-path: ./${{ matrix.os }} + + - name: Push benchmark result + if: ${{ github.event_name != 'pull_request' }} + run: | + cd benchmark-data-repository + git push https://${REPO_KEY}@github.com/metacall/core-benchmarks.git + env: + REPO_KEY: ${{secrets.BENCHMARKS_PUSH_TOKEN}} diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000000..243d5b2355 --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,19 @@ +name: Code Formatting Check + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + formatting-check: + name: Formatting Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run clang-format style check for C/C++. + uses: jidicula/clang-format-action@v4.9.0 + with: + clang-format-version: '11' + check-path: 'source' diff --git a/.github/workflows/docker-hub.yml b/.github/workflows/docker-hub.yml new file mode 100644 index 0000000000..34267338aa --- /dev/null +++ b/.github/workflows/docker-hub.yml @@ -0,0 +1,214 @@ +name: Build and Push Docker Image for Multiple Architectures + +on: + pull_request: + push: + branches: + - master + - develop + tags: + - 'v*.*.*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + DOCKER_REGISTRY: index.docker.io + DOCKER_USERNAME: metacall + IMAGE_NAME: core + BUILDKIT_VERSION: 0.13.0 + + # TODO: Tests failing + # - linux/s390x + # TODO: Detour not supported, needs to patch GOT instead of PLT + # - linux/mips64le + # - linux/mips64 + PLATFORM_LIST: > + [ + "linux/amd64", + "linux/amd64/v2", + "linux/amd64/v3", + "linux/386", + "linux/arm64", + "linux/riscv64", + "linux/ppc64le", + "linux/arm/v7", + "linux/arm/v6", + "linux/loong64" + ] + +jobs: + matrix: + name: Generate Platform List + runs-on: ubuntu-latest + outputs: + platform_list: ${{ steps.generate_platform_list.outputs.platform_list }} + steps: + - name: Generate platform list + id: generate_platform_list + run: | + set -exuo pipefail + PLATFORM_STRING=$(cat <> $GITHUB_ENV + echo "::set-output name=platform_list::$PLATFORM_LIST" + + build: + name: Build + runs-on: ubuntu-latest + needs: matrix + strategy: + fail-fast: false + matrix: + platform: ${{ fromJSON(needs.matrix.outputs.platform_list) }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker BuildX + uses: docker/setup-buildx-action@v3 + with: + version: v${{ env.BUILDKIT_VERSION }} + + - name: Login to Docker Hub + # Only run when master or when tagging a version + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Build MetaCall Docker Images + env: + METACALL_PLATFORM: ${{ matrix.platform }} + DOCKER_BUILDKIT: 1 + run: | + ./docker-compose.sh platform + + - name: Run Tests + env: + DOCKER_BUILDKIT: 1 + run: | + set -exuo pipefail + docker image inspect ${DOCKER_USERNAME}/${IMAGE_NAME}:cli --format='{{.Os}}/{{.Architecture}}' + cat < Dockerfile.test + FROM ${DOCKER_USERNAME}/${IMAGE_NAME}:cli + RUN apt-get update && apt-get install -y file + RUN file /usr/local/bin/metacallcli && ldd /usr/local/bin/metacallcli + RUN echo "console.log('0123456789abcdef')" > script.js + RUN metacallcli script.js | tee output.txt + RUN grep 0123456789abcdef output.txt + EOF + + docker buildx build --progress=plain --platform ${{ matrix.platform }} -f Dockerfile.test -t test-image . + + - name: Tag & Push Platform Images + # Only run when master or when tagging a version + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'pull_request' + run: | + platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-') + for tag in "deps" "dev" "runtime" "cli"; do + docker tag \ + ${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} \ + ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag} + + echo "Pushing image for tag: ${tag} with platform: ${platform_tag}" + docker push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag} + done + + manifest: + name: Create and Push Manifest Lists + needs: [matrix, build] + # Only run when master or when tagging a version + if: (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && github.event_name != 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Create and Push Manifest Lists + run: | + set -exuo pipefail + + tags=("deps" "dev" "runtime" "cli") + platforms=($(echo '${{ needs.matrix.outputs.platform_list }}' | jq -r '.[]')) + + echo "Create all the tags by platform" + + for tag in "${tags[@]}"; do + echo "Creating manifest for tag: $tag" + platform_tags="" + for platform in "${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}" + done + echo "Creating manifest with tags: ${platform_tags}" + docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} ${platform_tags} --amend + docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag} + done + + echo "Create the latest tag" + + cli_platform_tags="" + for platform in ${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + cli_platform_tags="${cli_platform_tags} ${DOCKER_USERNAME}/${IMAGE_NAME}:cli-${platform_tag}" + done + docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:latest ${cli_platform_tags} --amend + docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:latest + + if [[ "${{ startsWith(github.ref, 'refs/tags/') }}" = true ]]; then + VERSION=${GITHUB_REF#refs/tags/v} + + echo "Create the version ${VERSION} tag" + + for tag in "${tags[@]}"; do + platform_tags="" + for platform in "${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + platform_tags="${platform_tags} ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${tag}-${platform_tag}" + done + docker manifest create ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${VERSION}-${tag} ${platform_tags} --amend + docker manifest push ${DOCKER_REGISTRY}/${DOCKER_USERNAME}/${IMAGE_NAME}:${VERSION}-${tag} + done + fi + + cleanup: + name: Cleanup Platform Specific Tags + needs: [matrix, build, manifest] + runs-on: ubuntu-latest + if: always() + steps: + - name: Remove Platform-Specific Tags + run: | + set -exuo pipefail + + tags=("deps" "dev" "runtime" "cli") + platforms=($(echo '${{ needs.matrix.outputs.platform_list }}' | jq -r '.[]')) + + for tag in "${tags[@]}"; do + for platform in "${platforms[@]}"; do + platform_tag=$(echo "${platform}" | tr '/' '-') + tag_to_delete="${tag}-${platform_tag}" + + echo "Deleting tag: ${tag_to_delete}" + echo "https://hub.docker.com/v2/repositories/$%7BDOCKER_USERNAME%7D/$%7BIMAGE_NAME%7D/tags/$%7Btag_to_delete%7D/" + + curl -X DELETE \ + -H "Authorization: Bearer ${{ secrets.DOCKER_HUB_ACCESS_TOKEN_DELETE }}" \ + "https://hub.docker.com/v2/repositories/$%7BDOCKER_USERNAME%7D/$%7BIMAGE_NAME%7D/tags/$%7Btag_to_delete%7D/" + done + done diff --git a/.github/workflows/linux-test.yml b/.github/workflows/linux-test.yml new file mode 100644 index 0000000000..dd9af86734 --- /dev/null +++ b/.github/workflows/linux-test.yml @@ -0,0 +1,85 @@ +name: Linux Test + +on: + workflow_dispatch: + pull_request: + push: + tags: + - 'v*.*.*' + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + linux-test: + name: Linux GCC Test + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + build: [debug, release] + image: ["debian:trixie-slim", "debian:bookworm-slim", "ubuntu:noble", "ubuntu:jammy"] # TODO: "alpine:3.17" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install, build and run tests (build) + run: ./docker-compose.sh build + env: + METACALL_BUILD_TYPE: ${{ matrix.build }} + METACALL_BASE_IMAGE: ${{ matrix.image }} + + - name: Install, build and run tests (test) + run: ./docker-compose.sh test + env: + METACALL_BUILD_TYPE: ${{ matrix.build }} + METACALL_BASE_IMAGE: ${{ matrix.image }} + + linux-sanitizer-test: + name: Linux GCC Sanitizer Test + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + image: ["debian:trixie-slim", "debian:bookworm-slim", "ubuntu:noble", "ubuntu:jammy"] + sanitizer: [address-sanitizer, thread-sanitizer] # TODO: memory-sanitizer not supported by GCC + + env: + SANITIZER_SKIP_SUMMARY: 1 + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install, build and run thread sanitizer tests + run: ./docker-compose.sh test-${{ matrix.sanitizer }} + env: + METACALL_BUILD_TYPE: debug + METACALL_BASE_IMAGE: ${{ matrix.image }} + + linux-distributable: + name: Linux Distributable Dispatch + needs: [linux-test, linux-sanitizer-test] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' + steps: + - name: Linux Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: distributable-linux + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: ci.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: master diff --git a/.github/workflows/macos-test.yml b/.github/workflows/macos-test.yml new file mode 100644 index 0000000000..520de15171 --- /dev/null +++ b/.github/workflows/macos-test.yml @@ -0,0 +1,127 @@ +name: MacOS Test + +on: + workflow_dispatch: + pull_request: + push: + tags: + - "v*.*.*" + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + macos-test: + name: MacOS Clang Test + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [macos-13, macos-14, macos-15] + options: [ + {build: debug, sanitizer: without-sanitizer}, + {build: debug, sanitizer: address-sanitizer}, + {build: debug, sanitizer: thread-sanitizer}, + {build: release, sanitizer: without-sanitizer} + ] + + env: + NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Uninstall CMake + run: | + brew uninstall --force cmake + + - name: Uninstall NodeJS and NPM + run: | + npm uninstall npm -g + rm -rf /usr/local/lib/node_modules/npm + + - name: Uninstall Ruby + run: | + brew uninstall --force --ignore-dependencies ruby + brew cleanup -s ruby + brew cleanup --prune-prefix + RUBY_FRAMEWORK_DIR=$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/Ruby.framework + sudo rm -rf $RUBY_FRAMEWORK_DIR + + - name: Uninstall Go + run: | + brew uninstall --force go + brew autoremove + sudo rm -rf /usr/local/Cellar/go + sudo rm -rf /usr/local/go + sudo rm -rf /usr/local/opt/go + sudo rm -rf /etc/paths.d/go + sudo rm -rf /usr/local/bin/go + sudo rm -rf /usr/local/bin/gofmt + + - name: Uninstall Java + run: | + sudo rm -rf /Library/Java/JavaVirtualMachines/* + sudo rm -rf /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin + sudo rm -rf /Library/PreferencePanes/JavaControlPanel.prefPane + unset JAVA_HOME + + - name: Export XCode SDK Root + run: echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV + + - name: Set up the environment + run: sh ./tools/metacall-environment.sh $METACALL_INSTALL_OPTIONS + env: + METACALL_INSTALL_OPTIONS: base python nodejs typescript java ruby wasm rpc file cobol go backtrace #netcore5 c rust rapidjson pack # clangformat v8rep51 coverage + + - name: Configure + run: | + cd build + . .env + bash ../tools/metacall-configure.sh $METACALL_CONFIGURE_OPTIONS + env: + METACALL_CONFIGURE_OPTIONS: ${{ matrix.options.build }} ${{ matrix.options.sanitizer }} scripts ports tests python nodejs typescript java ruby wasm rpc file cobol go benchmarks install # netcore5 c rust examples pack # v8 coverage + + - name: Build + working-directory: ./build + run: | + . .env + bash ../tools/metacall-build.sh $METACALL_BUILD_OPTIONS + env: + METACALL_BUILD_OPTIONS: ${{ matrix.options.build }} tests install + + macos-distributable: + name: MacOS Distributable Dispatch + needs: macos-test + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' + steps: + - name: Homebrew Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: homebrew + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: test.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: main + - name: MacOS Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: distributable-macos + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: ci.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: master diff --git a/.github/workflows/release-nodejs.yml b/.github/workflows/release-nodejs.yml new file mode 100644 index 0000000000..da626217cc --- /dev/null +++ b/.github/workflows/release-nodejs.yml @@ -0,0 +1,26 @@ +name: Release NodeJS Package + +on: + push: + branches: [ master, develop ] + paths: + - 'source/ports/node_port/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + release: + name: Release NodeJS Port + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release the port + working-directory: source/ports/node_port + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: ./upload.sh diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml new file mode 100644 index 0000000000..a5f0c7de4f --- /dev/null +++ b/.github/workflows/release-python.yml @@ -0,0 +1,28 @@ +name: Release Python Package + +on: + push: + branches: [ master, develop ] + paths: + - 'source/ports/py_port/**' + +permissions: + id-token: write + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + release: + name: Release Python Port + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release the port + working-directory: source/ports/py_port + run: ./upload.sh diff --git a/.github/workflows/release-rust.yml b/.github/workflows/release-rust.yml new file mode 100644 index 0000000000..d2a0c31a43 --- /dev/null +++ b/.github/workflows/release-rust.yml @@ -0,0 +1,66 @@ +name: Release Rust Crates + +on: + workflow_dispatch: + pull_request: + push: + branches: [ master, develop ] + paths: + - '.github/workflows/release-rust.yml' + - 'source/ports/rs_port/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + +jobs: + test: + name: Rust Port Tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] # TODO: , windows-latest] + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install MetaCall Unix + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' + run: curl -sL https://raw.githubusercontent.com/metacall/install/master/install.sh | sh + - name: Install MetaCall Windows + if: matrix.os == 'windows-latest' + run: powershell -NoProfile -ExecutionPolicy Unrestricted -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://raw.githubusercontent.com/metacall/install/master/install.ps1')))" + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build the Rust Port + working-directory: source/ports/rs_port + run: cargo build --verbose + + - name: Test the Rust Port + working-directory: source/ports/rs_port + run: cargo test --verbose + + release: + name: Release Rust Port + runs-on: ubuntu-latest + needs: test + if: ${{ github.event_name != 'pull_request' }} + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Release the port + run: | + cd source/ports/rs_port + bash ./upload.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..24f963c9e9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GHR_VERSION: 0.17.0 + IMAGE_NAME: index.docker.io/metacall/core + IMAGE_REGISTRY: index.docker.io + ARTIFACTS_PATH: ./build-artifacts + # GITHUB_TOKEN - From default secrets + # GITHUB_REPOSITORY - Default variable + +jobs: + build: + name: Build the core + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Pull the Docker images + run: bash ./docker-compose.sh pull + - name: Build the Docker images + run: bash ./docker-compose.sh build + - name: Extract built artifacts + run: bash ./docker-compose.sh pack + - name: Upload built artifacts + uses: actions/upload-artifact@v4 + with: + name: built-artifacts + path: ${{ env.ARTIFACTS_PATH }}/ + + release: + name: Release on GitHub + runs-on: ubuntu-latest + needs: build + steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 # To fetch all tags + - name: Download built artifacts + uses: actions/download-artifact@v4.1.8 + with: + name: built-artifacts + path: ${{ env.ARTIFACTS_PATH }}/ + - name: Load GHR binary + run: | + curl -sL https://github.com/tcnksm/ghr/releases/download/v${GHR_VERSION}/ghr_v${GHR_VERSION}_linux_amd64.tar.gz | tar zx + chmod +x ghr_v${GHR_VERSION}_linux_amd64/ghr + mv ghr_v${GHR_VERSION}_linux_amd64/ghr /usr/local/bin + - name: Export variables + run: | + echo "GH_REPO_OWNER=${GITHUB_REPOSITORY_OWNER}" >> $GITHUB_ENV + echo "GH_REPO_NAME=${GITHUB_REPOSITORY#*/}" >> $GITHUB_ENV + export PREVIOUS_TAG=$(git describe HEAD^1 --abbrev=0 --tags) + echo "PREVIOUS_TAG=${PREVIOUS_TAG}" >> $GITHUB_ENV + echo "GIT_HISTORY<> $GITHUB_ENV + echo "$(git log --no-merges --format="- %s" ${PREVIOUS_TAG}..HEAD)" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + - name: Release on GitHub + run: | + if [[ "${PREVIOUS_TAG}" == "" ]]; then export GIT_HISTORY=$(git log --no-merges --format="- %s"); fi + export CI_COMMIT_TAG="${{ github.ref_name }}" + export RELEASE_DATE=$(date '+%Y-%m-%d') + echo "MetaCall ${CI_COMMIT_TAG} [${RELEASE_DATE}]" + ghr -t "${{ secrets.GITHUB_TOKEN }}" -u "${GH_REPO_OWNER}" -r "${GH_REPO_NAME}" -c "${GITHUB_SHA}" -n "MetaCall ${CI_COMMIT_TAG} [${RELEASE_DATE}]" -b "${GIT_HISTORY}" -replace "${CI_COMMIT_TAG}" "${PWD}/${{ env.ARTIFACTS_PATH }}/packages/" diff --git a/.github/workflows/windows-test.yml b/.github/workflows/windows-test.yml new file mode 100644 index 0000000000..3518e6d4db --- /dev/null +++ b/.github/workflows/windows-test.yml @@ -0,0 +1,84 @@ +name: Windows Test + +on: + workflow_dispatch: + pull_request: + push: + tags: + - 'v*.*.*' + branches: + - master + - develop + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + windows-test: + name: Windows MSVC Test + runs-on: windows-${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [2022, 2025] + options: [ + {build: debug, sanitizer: without-sanitizer}, + {build: debug, sanitizer: address-sanitizer}, + + # TODO: Not supported yet by MSVC + # {build: debug, sanitizer: thread-sanitizer}, + # {build: debug, sanitizer: memory-sanitizer}, + + # TODO: https://github.com/metacall/core/issues/461 + # {build: release, sanitizer: without-sanitizer} + ] + + steps: + - name: Check out the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Activate the Visual Studio Developer Prompt + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64 + + - name: Set up the environment + run: cmd.exe /c "powershell .\tools\metacall-environment.ps1 $Env:METACALL_INSTALL_OPTIONS" + env: + METACALL_INSTALL_OPTIONS: python nodejs java ruby typescript wasm rpc file # netcore5 java c cobol rust rapidjson pack # clangformat v8rep51 coverage + + - name: Configure + run: | + $METACALL_PATH = $PWD + md -Force "$METACALL_PATH\build" + cd "$METACALL_PATH\build" + cmd.exe /c "powershell ..\tools\metacall-configure.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: ${{ matrix.options.build }} ${{ matrix.options.sanitizer }} scripts ports tests python nodejs java ruby typescript wasm rpc file # netcore5 java c cobol rust examples install pack benchmarks # v8 coverage + + - name: Build + working-directory: ./build + run: cmd.exe /c "powershell ..\tools\metacall-build.ps1 $Env:METACALL_BUILD_OPTIONS" + env: + METACALL_BUILD_OPTIONS: ${{ matrix.options.build }} tests install + + windows-distributable: + name: Windows Distributable Dispatch + needs: windows-test + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' + steps: + - name: Windows Workflow Dispatch + uses: convictional/trigger-workflow-and-wait@v1.6.1 + with: + owner: metacall + repo: distributable-windows + github_token: ${{ secrets.G_PERSONAL_ACCESS_TOKEN }} + workflow_file_name: ci.yml + wait_workflow: true + client_payload: '{"ref": "${{ github.head_ref || github.ref_name }}"}' + ref: master diff --git a/.gitignore b/.gitignore index 4cfe1a0fcc..f462998c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ release_build /bin /lib /install +/dependencies # CMake CMakeCache.txt @@ -32,14 +33,6 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake -# Avoid ignoring build hooks -!hooks/build -!tools/base/hooks/build -!tools/core/hooks/build -!tools/dev/hooks/build -!tools/node/hooks/build -!tools/cli/hooks/build - # Qt cache CMakeLists.txt.user CMakeLists.txt.user.* @@ -54,15 +47,21 @@ _open-project.bat _start-cmake-gui.bat _start-cmd.bat +# macOS files +.DS_Store + # Local config unix .localconfig # Visual Studio Code .vscode +# Clang files +.cache + # Linked dockerignore file in main context .dockerignore -!tools/base/.dockerignore -!tools/core/.dockerignore +!tools/deps/.dockerignore !tools/dev/.dockerignore -!tools/node/.dockerignore +!tools/runtime/.dockerignore +!tools/cli/.dockerignore diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index a7e60dfdf3..0000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -# -# MetaCall Library by Parra Studios -# GitLab CD/CI infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -services: - - docker:dind - -before_script: - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - -stages: - - build - - package - -variables: - GIT_SUBMODULE_STRATEGY: recursive - IMAGE_REGISTRY: registry.gitlab.com - IMAGE_NAME: registry.gitlab.com/$CI_PROJECT_PATH - DOCKER_HOST: tcp://docker:2375 - DOCKER_DRIVER: overlay2 - ARTIFACTS_PATH: $CI_PROJECT_DIR/artifacts - -build: - stage: build - image: - name: docker/compose:1.23.2 - entrypoint: [""] - script: - - $CI_PROJECT_DIR/docker-compose.sh pull - - $CI_PROJECT_DIR/docker-compose.sh cache - - $CI_PROJECT_DIR/docker-compose.sh push - only: - - master - -# TODO: Implement tags correctly for Docker -package: - stage: package - image: - name: docker/compose:1.23.2 - entrypoint: [""] - script: - - $CI_PROJECT_DIR/docker-compose.sh pull - - $CI_PROJECT_DIR/docker-compose.sh cache - - $CI_PROJECT_DIR/docker-compose.sh push - - $CI_PROJECT_DIR/docker-compose.sh pack - artifacts: - paths: - - $CI_PROJECT_DIR/artifacts/packages - only: - - tags diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000000..9d72770244 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,23 @@ +tasks: + +- name: Build Metacall + init: | + mkdir build && cd ./build + cmake .. + cd .. + command: + sudo env PATH=$PATH cmake --build . --target install + +- name: Pulling metacall Docker Images + init: | + docker pull metacall/core + docker pull metacall/core:dev + mkdir -p $HOME/metacall + command: + docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:dev /bin/bash + +- name: Pulling metacall-cli Docker Image + init: | + docker pull metacall/core:cli + command: + docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:cli diff --git a/.nanorc b/.nanorc deleted file mode 100644 index 4e2af9d135..0000000000 --- a/.nanorc +++ /dev/null @@ -1,23 +0,0 @@ -# -# MetaCall Nano Configuration by Parra Studios -# A configuration for nano text editor. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -set tabsize 4 # Define tab size to 4 -set autoindent # Use auto-indentation -set casesensitive # Do case sensitive searches by default -set whitespace "¬." # Show whitespaces diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e6650d43e9..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,87 +0,0 @@ -# -# MetaCall Library by Parra Studios -# Travis CD/CI infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -language: cpp - -branches: - only: - - master # TODO: Change to master or tags - -sudo: required - -services: - - docker - -dist: xenial - -# Global environment variables -env: - global: - - DOCKER_VERSION: 18.06.1 - - DOCKER_COMPOSE_VERSION: 1.22.0 - - GHR_VERSION: 0.12.0 - - GIT_SUBMODULE_STRATEGY: recursive - - IMAGE_REGISTRY: registry.gitlab.com - - IMAGE_NAME: registry.gitlab.com/$TRAVIS_REPO_SLUG - - ARTIFACTS_PATH: $TRAVIS_BUILD_DIR/artifacts - -# Update Docker & Docker Compose -before_script: - - sudo rm /usr/local/bin/docker-compose || true - - curl -sL https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose - - chmod +x docker-compose - - sudo mv docker-compose /usr/local/bin - - sudo sh -c 'echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list' - - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - - sudo apt-key fingerprint 0EBFCD88 - - sudo apt-get update - - sudo apt-get -y install docker-ce=${DOCKER_VERSION}~ce~3-0~ubuntu # -$(lsb_release -cs) - -# Run the build and packaging steps with cache -script: - - $TRAVIS_BUILD_DIR/docker-compose.sh pull - - $TRAVIS_BUILD_DIR/docker-compose.sh cache - - $TRAVIS_BUILD_DIR/docker-compose.sh pack - -# Custom script for deploying to GitHub the artifacts -after_script: - - curl -sL https://github.com/tcnksm/ghr/releases/download/v${GHR_VERSION}/ghr_v${GHR_VERSION}_linux_amd64.tar.gz | tar zx - - chmod +x ghr_v${GHR_VERSION}_linux_amd64/ghr - - sudo mv ghr_v${GHR_VERSION}_linux_amd64/ghr /usr/local/bin - - TRAVIS_REPO_OWNER=${TRAVIS_REPO_SLUG%/*} - - TRAVIS_REPO_NAME=${TRAVIS_REPO_SLUG#*/} - - PREVIOUS_TAG=`git describe HEAD^1 --abbrev=0 --tags` - - GIT_HISTORY=`git log --no-merges --format="- %s" ${PREVIOUS_TAG}..HEAD` - - if [[ $PREVIOUS_TAG == "" ]]; then GIT_HISTORY=`git log --no-merges --format="- %s"`; fi - - if [[ $TRAVIS_TAG == "" ]]; then TRAVIS_TAG=${PREVIOUS_TAG}; fi - - RELEASE_DATE=`date '+%Y-%m-%d'` - - echo "MetaCall ${TRAVIS_TAG} [${RELEASE_DATE}]" - - | - ghr -t $GITHUB_TOKEN -u $TRAVIS_REPO_OWNER -r $TRAVIS_REPO_NAME -c $TRAVIS_COMMIT \ - -n "MetaCall ${TRAVIS_TAG} [${RELEASE_DATE}]" -b "${GIT_HISTORY}" -replace \ - $TRAVIS_TAG $ARTIFACTS_PATH/packages/ - -# Deploy artifacts to bintray -deploy: - provider: bintray - file: "$TRAVIS_BUILD_DIR/deploy/packages/descriptor-metacall.json" - user: "${BINTRAY_USER}" - key: "${BINTRAY_KEY}" - skip_cleanup: true - on: master # TODO: Change to master or tags diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b2472efa4..f543559e6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # A library for providing a foreing function interface calls. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,19 +22,15 @@ # # CMake version -cmake_minimum_required(VERSION 3.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) # Include cmake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(WriterCompilerDetectionHeaderFound NOTFOUND) -include(GenerateExportHeader) include(ExternalProject) -# This module is only available with CMake >=3.1, so check whether it could be found -include(WriteCompilerDetectionHeader OPTIONAL RESULT_VARIABLE WriterCompilerDetectionHeaderFound) - include(GetGitRevisionDescription) include(Custom) @@ -43,6 +39,7 @@ set_policy(CMP0028 NEW) # ENABLE CMP0028: Double colon in target name means ALIA set_policy(CMP0054 NEW) # ENABLE CMP0054: Only interpret if() arguments as variables or keywords when unquoted. set_policy(CMP0042 NEW) # ENABLE CMP0042: MACOSX_RPATH is enabled by default. set_policy(CMP0063 NEW) # ENABLE CMP0063: Honor visibility properties for all target types. +set_policy(CMP0135 NEW) # ENABLE CMP0135: Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24. # # Project description and (meta) information @@ -58,11 +55,15 @@ set(META_PROJECT_DESCRIPTION "A library for providing inter-language foreign fun set(META_AUTHOR_ORGANIZATION "MetaCall Inc.") set(META_AUTHOR_DOMAIN "https://metacall.io/") set(META_AUTHOR_MAINTAINER "vic798@gmail.com") -set(META_VERSION_MAJOR "0") -set(META_VERSION_MINOR "1") -set(META_VERSION_PATCH "0") + +# Parse version +file(READ VERSION META_VERSION) +string(REPLACE "\n" "" META_VERSION ${META_VERSION}) +string(REPLACE "." ";" META_VERSION_LIST ${META_VERSION}) +list(GET META_VERSION_LIST 0 META_VERSION_MAJOR) +list(GET META_VERSION_LIST 1 META_VERSION_MINOR) +list(GET META_VERSION_LIST 2 META_VERSION_PATCH) set(META_VERSION_REVISION "${GIT_REV}") -set(META_VERSION "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}") set(META_NAME_VERSION "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_REVISION})") # @@ -70,21 +71,28 @@ set(META_NAME_VERSION "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_R # # Project options -option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) -option(OPTION_BUILD_DIST_LIBS "Build all libraries into a single compilation unit." ON) -option(OPTION_SELF_CONTAINED "Create a self-contained install with all dependencies." OFF) +option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) # TODO: Static libraries build is not implemented yet +option(OPTION_SELF_CONTAINED "Create a self-contained install with all dependencies." OFF) # TODO: Not implemented option(OPTION_BUILD_TESTS "Build tests." ON) option(OPTION_BUILD_BENCHMARKS "Build benchmarks." OFF) option(OPTION_BUILD_DOCS "Build documentation." OFF) option(OPTION_BUILD_EXAMPLES "Build examples." ON) +option(OPTION_BUILD_CLI "Build CLIs." ON) option(OPTION_BUILD_LOADERS "Build loaders." ON) +option(OPTION_BUILD_EXTENSIONS "Build extensions." ON) option(OPTION_BUILD_SCRIPTS "Build scripts." ON) option(OPTION_BUILD_SERIALS "Build serials." ON) option(OPTION_BUILD_DETOURS "Build detours." ON) option(OPTION_BUILD_PORTS "Build ports." OFF) -option(OPTION_FORK_SAFE "Enable fork safety." OFF) +option(OPTION_BUILD_LOG_PRETTY "Build logs in a human readable format." ON) +option(OPTION_BUILD_PIC "Build with position independent code." ON) +option(OPTION_BUILD_SECURITY "Build with stack-smashing protection and source fortify." ON) +option(OPTION_BUILD_GUIX "Disable all build system unreproductible operations." OFF) +option(OPTION_GIT_HOOKS "Disable git hooks when running in CI/CD." ON) +option(OPTION_FORK_SAFE "Enable fork safety." ON) option(OPTION_THREAD_SAFE "Enable thread safety." OFF) option(OPTION_COVERAGE "Enable coverage." OFF) +option(OPTION_MEMORY_TRACKER "Enable memory tracking for reflect data." ON) # Build type if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) @@ -92,6 +100,13 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") endif() +# Binary directory +if ("${CMAKE_GENERATOR}" MATCHES "^(Visual Studio)") + set(PROJECT_OUTPUT_DIR "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") +else() + set(PROJECT_OUTPUT_DIR "${CMAKE_BINARY_DIR}") +endif() + # # Declare project # @@ -111,12 +126,42 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) # Create version file file(WRITE "${PROJECT_BINARY_DIR}/VERSION" "${META_NAME_VERSION}") +# +# Define Export Headers +# + +include(GenerateExportHeader) + +# Patch export headers in order to produce "${target_upper}_EXPORTS" in API definition +function(GENERATE_EXPORT_HEADER) + # This function is patched again in Portability, due to Haiku workaround, review it if this func gets removed + set(target ${ARGV0}) + string(TOUPPER ${target} target_upper) + set_target_properties(${target} + PROPERTIES DEFINE_SYMBOL "${target_upper}_EXPORTS" + ) + # When the function is redefined, the old function can be accessed through underscore + _GENERATE_EXPORT_HEADER(${ARGN}) +endfunction() + # # Portability # include(Portability) +# +# clang-format and tidy +# + +find_package(ClangFormat) + +if(ClangFormat_FOUND) + include(ClangDevTools) +else() + message(WARNING "Linting disabled: clang-format executable not found") +endif() + # # Compiler settings and options # @@ -128,12 +173,12 @@ include(CompileOptions) # if(OPTION_COVERAGE) - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/coverage") - - set(ENABLE_COVERAGE ON CACHE BOOL "Enable coverage build." FORCE) - set(ENABLE_COVERAGE_ALL ON CACHE BOOL "Enable coverage build for all targets." FORCE) - - find_package(codecov) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "OPTION_COVERAGE requires to be build with -DCMAKE_BUILD_TYPE=Debug, skipping coverage") + set(OPTION_COVERAGE OFF) + else() + include(Coverage) + endif() endif() # @@ -150,7 +195,7 @@ if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr" OR "${CMAKE_INSTALL_PREFIX}" STREQU endif() # Installation paths -if(UNIX AND SYSTEM_DIR_INSTALL) +if(UNIX AND SYSTEM_DIR_INSTALL OR OPTION_BUILD_GUIX) # Install into the system (/usr/bin or /usr/local/bin) set(INSTALL_ROOT "share/${project}") # /usr/[local]/share/ set(INSTALL_CMAKE "share/${project}/cmake") # /usr/[local]/share//cmake @@ -190,32 +235,46 @@ if(SYSTEM_DIR_INSTALL) else() # Find libraries relative to binary if(APPLE) - set(CMAKE_INSTALL_RPATH "@loader_path/../../../${INSTALL_LIB}") + set(CMAKE_INSTALL_RPATH "@loader_path/${INSTALL_LIB}") else() set(CMAKE_INSTALL_RPATH "$ORIGIN/${INSTALL_LIB}") endif() endif() +# Export compile commands +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # # CTest configuration # -if(OPTION_BUILD_TESTS) +if(OPTION_BUILD_TESTS OR OPTION_BUILD_BENCHMARKS) enable_testing() endif() # -# Project module names +# Project module includes +# + +add_subdirectory(source) +add_subdirectory(docs) +add_subdirectory(deploy) + +# +# Project configuration generation # -set(MODULE_NAMES +set(EXPORT_MODULE_NAMES version preprocessor environment format + threading log memory + portability adt + filesystem reflect dynlink detour @@ -225,48 +284,14 @@ set(MODULE_NAMES metacall ) -# -# Project export module names -# - -if(NOT OPTION_BUILD_DIST_LIBS) - set(EXPORT_MODULE_NAMES - ${MODULE_NAMES} - ) -else() - set(EXPORT_MODULE_NAMES - metacall - ) -endif() - -# -# Project module includes -# - -add_subdirectory(source) -add_subdirectory(docs) -add_subdirectory(deploy) - -# -# Coverage evaluation (must run after all targets) -# - -if(OPTION_COVERAGE) - coverage_evaluate() -endif() - -# -# Project configuration generation -# - set(PROJECT_CONFIGURATION "metacall-config.cmake") set(PROJECT_CONFIGURATION_VERSION "metacall-config-version.cmake") set(PROJECT_CONFIGURATION_PATH "${PROJECT_BINARY_DIR}/${PROJECT_CONFIGURATION}") set(PROJECT_CONFIGURATION_VERSION_PATH "${PROJECT_BINARY_DIR}/${PROJECT_CONFIGURATION_VERSION}") -configure_file("${PROJECT_CONFIGURATION}.in" "${PROJECT_CONFIGURATION_PATH}" @ONLY) -configure_file("${PROJECT_CONFIGURATION_VERSION}.in" "${PROJECT_CONFIGURATION_VERSION_PATH}" @ONLY) +configure_file("${CMAKE_SOURCE_DIR}/${PROJECT_CONFIGURATION}.in" "${PROJECT_CONFIGURATION_PATH}" @ONLY) +configure_file("${CMAKE_SOURCE_DIR}/${PROJECT_CONFIGURATION_VERSION}.in" "${PROJECT_CONFIGURATION_VERSION_PATH}" @ONLY) # # Deployment (global project files) @@ -286,3 +311,15 @@ install(FILES README.md DESTINATION ${INSTALL_ROOT} COMPONENT runtime) # # Install runtime data # install(DIRECTORY ${PROJECT_SOURCE_DIR}/data DESTINATION ${INSTALL_DATA} COMPONENT runtime) + +# +# Install githooks directory +# + +if(OPTION_GIT_HOOKS) + message(STATUS "Installing git hooks at ${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND git config --local core.hooksPath githooks/${PROJECT_OS_FAMILY} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b8934f6f1f..014aa2a803 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -6,3 +6,14 @@ Ajith Ramachandran Akash Joshi Thomas Rory Gummerson Hadi Azami +Declan Nnadozie +Duxz +Swarnim Arun +Ben Schattinger +Muhammad Tabaza +Anas Al Barghouthy +ketangupta34 +onkardahale +Akshit Garg +burnerlee +Praveen Kumar \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0f7655c850..a81cfd453b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/LICENSE b/LICENSE index eac8221d8d..d56ad81fad 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2016-2019 Vicente Eduardo Ferrer Garcia + Copyright 2016-2025 Vicente Eduardo Ferrer Garcia Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..687413ccf2 --- /dev/null +++ b/NOTICE @@ -0,0 +1,88 @@ +# Licenses Used In MetaCall (Optional) + +All external code and licenses used by **METACALL** are always wrapped into plugins and linked dynamically to them. By this reason the **METACALL** core is not polluted with licenses or software restrictions. The developer __**can choose**__ what plugins wants to use in order to fit their requirements. All license requirements are **optional**. + +# Table Of Contents + + + +- [Licenses Used In MetaCall (Optional)](#licenses-used-in-metacall-optional) +- [Table Of Contents](#table-of-contents) + - [1. Loaders](#1-loaders) + - [1.1 Python](#11-python) + - [1.2 NodeJS](#12-nodejs) + - [1.3 JavaScript (V8)](#13-javascript-v8) + - [1.4 C#](#14-c) + - [1.5 Ruby](#15-ruby) + - [1.6 C/C++](#16-cc) + - [1.6 JavaScript (SpiderMonkey)](#16-javascript-spidermonkey) + - [2. Serials](#2-serials) + - [2.1 RapidJSON](#21-rapidjson) + - [3. Detours](#3-detours) + - [3.1 PLTHook](#31-plthook) + - [4. Ports](#4-ports) + + + +## 1. Loaders + +### 1.1 Python + +| Software | License | +| :--------: | :--------------------------------------------------------------------------: | +| **Python** | [Python Software Foundation License](https://docs.python.org/3/license.html) | + +### 1.2 NodeJS + +| Software | License | +| :--------: | :---------------------------------------------------------------: | +| **NodeJS** | [MIT License](https://github.com/nodejs/node/blob/master/LICENSE) | + +### 1.3 JavaScript (V8) + +| Software | License | +| :------: | :------------------------------------------------------------------: | +| **V8** | [BSD 3-clause License](https://github.com/v8/v8/blob/master/LICENSE) | + +### 1.4 C# # + +| Software | License | +| :---------: | :---------------------------------------------------------------------------: | +| **NetCore** | [MIT License License](https://github.com/dotnet/core/blob/master/LICENSE.TXT) | + +### 1.5 Ruby + +| Software | License | +| :------: | :-----------------------------------------------------------------------------------: | +| **Ruby** | [BSD 2-clause License - Ruby License](https://www.ruby-lang.org/en/about/license.txt) | + +### 1.6 C/C++ + +| Software | License | +| :------------------: | :-----------------------------------------------------------------------------------------: | +| **Clang** - **LLVM** | [University of Illinois/NCSA Open Source License](http://releases.llvm.org/2.8/LICENSE.TXT) | +| **LibFFI** | [MIT License](https://github.com/libffi/libffi/blob/master/LICENSE) | + +### 1.6 JavaScript (SpiderMonkey) + +| Software | License | +| :--------------: | :-------------------------------------------------------------------------------: | +| **SpiderMonkey** | [Mozilla Public License 2.0](https://www.mozilla.org/en-US/foundation/licensing/) | + +## 2. Serials + +### 2.1 RapidJSON + +| Software | License | +| :-----------: | :-------------------------------------------------------------------------: | +| **RapidJSON** | [MIT License](https://github.com/Tencent/rapidjson/blob/master/license.txt) | + +## 3. Detours + +### 3.1 PLTHook + +| Software | License | +| :----------: | :------------------------------------------------------------------------------------------: | +| **PLTHook** | [2-clause BSD-style license](https://github.com/metacall/plthook?tab=readme-ov-file#license) | + +## 4. Ports diff --git a/NOTICE.md b/NOTICE.md deleted file mode 100644 index 64d3172e01..0000000000 --- a/NOTICE.md +++ /dev/null @@ -1,103 +0,0 @@ -# Licenses Used In MetaCall (Optional) - -All external code and licenses used by **METACALL** are always wrapped into plugins and linked dynamically to them. By this reason the **METACALL** core is not polluted with licenses or software restrictions. The developer __**can choose**__ what plugins wants to use in order to fit their requirements. All license requirements are **optional**. - -# Table Of Contents - - - -- [Licenses Used In MetaCall (Optional)](#licenses-used-in-metacall-optional) -- [Table Of Contents](#table-of-contents) - - [1. Loaders](#1-loaders) - - [1.1 Python](#11-python) - - [1.2 NodeJS](#12-nodejs) - - [1.3 JavaScript (V8)](#13-javascript-v8) - - [1.4 C#](#14-c) - - [1.5 Ruby](#15-ruby) - - [1.6 C/C++](#16-cc) - - [1.6 JavaScript (SpiderMonkey)](#16-javascript-spidermonkey) - - [2. Serials](#2-serials) - - [2.1 RapidJSON](#21-rapidjson) - - [3. Detours](#3-detours) - - [3.1 FuncHook](#31-funchook) - - [4. Ports](#4-ports) - - [4.1 Swig](#41-swig) - - - -## 1. Loaders - -### 1.1 Python - -| Software | License | -| :--------: | :--------------------------------------------------------------------------: | -| **Python** | [Python Software Foundation License](https://docs.python.org/3/license.html) | - -### 1.2 NodeJS - -| Software | License | -| :--------: | :---------------------------------------------------------------: | -| **NodeJS** | [MIT License](https://github.com/nodejs/node/blob/master/LICENSE) | - -### 1.3 JavaScript (V8) - -| Software | License | -| :------: | :------------------------------------------------------------------: | -| **V8** | [BSD 3-clause License](https://github.com/v8/v8/blob/master/LICENSE) | - -### 1.4 C# # - -| Software | License | -| :---------: | :---------------------------------------------------------------------------: | -| **NetCore** | [MIT License License](https://github.com/dotnet/core/blob/master/LICENSE.TXT) | - -### 1.5 Ruby - -| Software | License | -| :------: | :-----------------------------------------------------------------------------------: | -| **Ruby** | [BSD 2-clause License - Ruby License](https://www.ruby-lang.org/en/about/license.txt) | - -### 1.6 C/C++ - -| Software | License | -| :------------------: | :-----------------------------------------------------------------------------------------: | -| **Clang** - **LLVM** | [University of Illinois/NCSA Open Source License](http://releases.llvm.org/2.8/LICENSE.TXT) | -| **LibFFI** | [MIT License](https://github.com/libffi/libffi/blob/master/LICENSE) | - -### 1.6 JavaScript (SpiderMonkey) - -| Software | License | -| :--------------: | :-------------------------------------------------------------------------------: | -| **SpiderMonkey** | [Mozilla Public License 2.0](https://www.mozilla.org/en-US/foundation/licensing/) | - -## 2. Serials - -### 2.1 RapidJSON - -| Software | License | -| :-----------: | :-------------------------------------------------------------------------: | -| **RapidJSON** | [MIT License](https://github.com/Tencent/rapidjson/blob/master/license.txt) | - -## 3. Detours - -### 3.1 FuncHook - -| Software | License | -| :----------: | :-------------------------------------------------------------------------------------------------: | -| **FuncHook** | [GPLv2 or later with a GPL linking exception](https://github.com/kubo/funchook/blob/master/LICENSE) | - -## 4. Ports - -### 4.1 Swig - -| Software | License | -| :------: | :-----: | -| **SWIG** | **∅** | - ->When SWIG is used as it is distributed by the SWIG developers, its output is not governed by SWIG's license (including the GPL). SWIG's output contains code from three sources: -> -> - code generated by SWIG, which is not governed by copyright; -> - code copied from the SWIG library which is permissively licensed to be redistributed without restriction; -> - code derived from the user's input, which may be governed by the license of the code supplied by the user. -> ->So, while the input supplied to SWIG may affect the license of SWIG's output (e.g. if the input code is licensed under a copyleft or proprietary license), SWIG's license does not affect the license of the output. This is consistent with the FSF's FAQ entries on this subject ([GPLOutput](http://www.gnu.org/licenses/gpl-faq.html#GPLOutput) and [WhatCaseIsOutputGPL](http://www.gnu.org/licenses/gpl-faq.html#WhatCaseIsOutputGPL)), because the SWIG code copied into the output by SWIG is not GPL-licensed. diff --git a/README.md b/README.md index 07c4677225..8248f30942 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,27 @@
- M E T A C A L L -

M E T A C A L L

-

A library for providing inter-language foreign function interface calls

+ METACALL +

MetaCall Polyglot Runtime

+ MetaCall.io | + Install | + Docs
+
+ + -# Abstract + + -**METACALL** is a library that allows calling functions, methods or procedures between programming languages. With **METACALL** you can transparently execute code from / to any programming language, for example, call Python code from JavaScript code. + + + + + + + M E T A C A L L +
+ +**MetaCall** allows calling functions, methods or procedures between multiple programming languages. `sum.py` ``` python @@ -16,685 +31,28 @@ def sum(a, b): `main.js` ``` javascript -metacall_load_from_file('py', [ 'sum.py' ]); - -metacall('sum', 3, 4); // 7 -``` - -
- M E T A C A L L -
- -# Table Of Contents - - - -- [Abstract](#abstract) -- [Table Of Contents](#table-of-contents) - - [1. Motivation](#1-motivation) - - [2. Language Support (Backends)](#2-language-support-backends) - - [3. Use Cases](#3-use-cases) - - [3.1 Known Projects Using MetaCall](#31-known-projects-using-metacall) - - [4. Usage](#4-usage) - - [4.1 Installation](#41-installation) - - [4.2 Environment Variables](#42-environment-variables) - - [4.3 Examples](#43-examples) - - [5. Architecture](#5-architecture) - - [5.1 Overview](#51-overview) - - [5.1.1 Design Decisions](#511-design-decisions) - - [5.1.2 Modules](#512-modules) - - [5.2 Reflect](#52-reflect) - - [5.2.1 Type System](#521-type-system) - - [5.2.2 Values](#522-values) - - [5.2.3 Functions](#523-functions) - - [5.3 Plugins](#53-plugins) - - [5.3.1 Loaders](#531-loaders) - - [5.3.1.1 Python](#5311-python) - - [5.3.1.2 NodeJS](#5312-nodejs) - - [5.3.1.3 JavaScript](#5313-javascript) - - [5.3.1.4 C#](#5314-c) - - [5.3.1.5 Ruby](#5315-ruby) - - [5.3.1.6 Mock](#5316-mock) - - [5.3.1.7 File](#5317-file) - - [5.3.2 Serials](#532-serials) - - [5.3.2.1 MetaCall](#5321-metacall) - - [5.3.2.2 RapidJSON](#5322-rapidjson) - - [5.3.3 Detours](#533-detours) - - [5.3.3.1 FuncHook](#5331-funchook) - - [5.4 Ports](#54-ports) - - [5.5 Serialization](#55-serialization) - - [5.6 Memory Layout](#56-memory-layout) - - [5.7 Fork Model](#57-fork-model) - - [5.8 Threading Model](#58-threading-model) - - [5. Application Programming Interface (API)](#5-application-programming-interface-api) - - [6. Build System](#6-build-system) - - [6.1 Build Options](#61-build-options) - - [6.2 Coverage](#62-coverage) - - [7. Platform Support](#7-platform-support) - - [7.1 Docker Support](#71-docker-support) - - [7.1.1 Docker Development](#711-docker-development) - - [7.1.2 Docker Testing](#712-docker-testing) - - [8. License](#8-license) - - - -## 1. Motivation - -The **METACALL** project started time ago when I was coding a [Game Engine for an MMORPG](https://bitbucket.org/parrastudios/argentum-online-c). My idea was to provide an interface to allow other programmers extend the Game Engine easily. By that time, I was finishing the university so I decide to do my [Final Thesis](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-paper.pdf) and [Presentation](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-presentation.pdf) based on the plug-in system for my Game Engine. The Plugin Architecture designed for the Game Engine has similarities with **METACALL** although the architecture has been redefined and the code has been rewritten from scratch. After some refination of the system, I came up with **METACALL** and other use cases for the tool. Currently we are using **METACALL** to build a cutting edge FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on this technique to provide high scalability of the functions among multiple cores and **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern, a new technique I have developed to interconnect transparently functions in a distributed system based on this library. - -## 2. Language Support (Backends) - -This section describes all programming languages that **METACALL** supports, if you are interested in from what languages can be used **METACALL** you must go to [ports section](#54-ports). - -- Currently supported languages and run-times: - -| Language | Runtime | Version | Tag | -|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------|:------------------------:|:----:| -| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **>= 3.2 <= 3.7** | py | -| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **>= 8.11.1 <= 10.15.3** | node | -| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [V8](https://v8.dev/) | **5.1.117** | js | -| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/docs/blob/master/docs/core/tutorials/netcore-hosting.md) | **1.1.10** | cs | -| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **>= 2.1 <= 2.3** | rb | -| [Mock](/source/loaders/mock_loader) | **∅** | **0.1.0** | mock | - -- Languages and run-times under construction: - -| Language | Runtime | Tag | -|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|:----:| -| [Java](https://www.java.com/) | [JNI](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/) | java | -| [C/C++](http://www.cplusplus.com/) | [Clang](https://clang.llvm.org/) - [LLVM](https://llvm.org/) - [libffi](http://sourceware.org/libffi/) | c | -| [File](/source/loaders/file_loader) | **∅** | file | -| [Go](https://golang.org/) | Go Runtime | go | -| [Haskell](https://www.haskell.org/) | [Haskell FFI](https://wiki.haskell.org/GHC/Using_the_FFI) | hs | -| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [SpiderMonkey](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference) | jsm | -| [WebAssembly](https://webassembly.org/) | [WebAssembly Virtual Machine](https://github.com/WAVM/WAVM) | wasm | - -## 3. Use Cases - -**METACALL** can be used in the following cases: - -- Interconnect different technologies in the same project. It allows to have heterogeneous teams of developers working over same project in an isolated way and using different programming languages at the same time. - -- Embedding programming languages to existing softwares. Game Engines, 3D Editors like [Blender](https://www.blender.org/), among others can take benefit of **METACALL** and extend the core functionality with higher level programming languages (aka scripting). - -- Function as a Service. **METACALL** can be used to implement efficient FaaS architectures. We are using it to implement our own FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern and high performance function scalability thanks to this library. - -- Source code migrations. **METACALL** can wrap large and legacy code-bases, and provide an agnostic way to work with the codebase into a new programming language. Eventually the code can be migrated by parts, without need of creating a new project or stopping the production environment. Incremental changes can be done, solving the migration easily and with less time and effort. - -- Porting low level libraries to high level languages transparently. With **METACALL** you can get rid of extension APIs like Python C API or NodeJS N-API. You can call directly low level libraries from your high level languages without making a wrapper in C or C++ for it. - -As you can see, there are plenty of uses. **METACALL** introduces a new model of programming which allows a high interoperability between technologies. If you find any other use case just let us know about it with a Pull Request and we will add it to the list. - -## 3.1 Known Projects Using MetaCall - -- **[Acid Cam](https://www.facebook.com/AcidCam/)**: A software for video manipulation that distorts videos for generating art. [Acid Cam CLI](https://github.com/lostjared/acidcam-cli) uses **METACALL** to allow custom filters written in Python and easily embed Python programming language into its plugin system. - -## 4. Usage - -## 4.1 Installation - -Prior to try any example, you must have **METACALL** installed in your system. To install **METACALL** you have the following options. - -- [Download a release](/TODO). -- [Install via package manager](/TODO). -- [Build and install it manually](#6-build-system). -- [Pull it from DockerHub](/TODO). - -### 4.2 Environment Variables - -This environment variables are optional, in case that you want to modify default paths of **METACALL**. - -| Name | Description | Default Value | -|:-------------------------:|------------------------------------------------------------------|:--------------------------------:| -| **`DETOUR_LIBRARY_PATH`** | Directory where detour plugins to be loaded are located | **`detours`** | -| **`SERIAL_LIBRARY_PATH`** | Directory where serial plugins to be loaded are located | **`serials`** | -| **`CONFIGURATION_PATH`** | File path where the **METACALL** global configuration is located | **`configurations/global.json`** | -| **`LOADER_LIBRARY_PATH`** | Directory where loader plugins to be loaded are located | **`loaders`** | -| **`LOADER_SCRIPT_PATH`** | Directory where scripts to be loaded are located | **`${execution_path}`** ¹ | - -¹ **`${execution_path}`** defines the path where the program is executed, **`.`** in Linux. - -### 4.3 Examples - -- [MetaCall CLI](/source/examples/metacallcli). Example of a Command Language Interpreter based on MetaCall where you can load, unload scripts and call their functions. - -- [MetaCall Rotulin](https://github.com/metacall/rotulin). Example of a multi-language application built with **METACALL**. This application embeds a Django server with a Ruby DataBase and C# business layer based on ImageMagick. - -## 5. Architecture - -### 5.1 Overview - -#### 5.1.1 Design Decisions - -- To provide an high level API with a simple UX and to be easy to understand. - -- To work in high performance environments. - -- To be as cross-platform as possible. - -- To avoid to modify run-times directly or use the code inside **METACALL** in order to avoid maintaining them, or propagating security flaws or licenses into **METACALL**. - -- To provide support for any embeddable programming language and to provide support for **METACALL** to be used form any programming language. - -- All external code used into **METACALL** must be introduced by inversion of control in the plugin system, so that the core must not remain aware from what software is using. - -- All code developed in **METACALL** must be implemented in standalone libraries that can work by itself in an isolated way (aka modules). - -#### 5.1.2 Modules - -- [`adt`](/source/adt) provides a base for Abstract Data Types and algorithms used in **METACALL**. Implementation must be done in an efficient and generic way. Some of the data structures implemented are vector, set, hash, comparable or trie. - -- [`detour`](/source/detour) provides an interface to hook into functions. Detours are used by the [fork model](#57-fork-model) to intercept fork calls. - -- [`detours`](/source/detours) implement the [`detour`](/source/detour) interface by using a plugin architecture. The current list of available detour plugins is the following one. - - [`funchook_detour`](/source/detours/funchook_detour) implemented by means of FuncHook library. - -- [`distributable`](/source/distributable) defines the compilation of **METACALL** that generates an unique library with all core libraries bundled into it. As the **METACALL** architecture is divided by modules, in order to distribute **METACALL** is needed to build all of them into a single library. This module implements this compilation by means of CMake. - -- [`dynlink`](/source/dynlink) implements a cross-platform method to dynamically load libraries. It is used to dynamically load plugins into **METACALL**. - -- [`environment`](/source/environment) implements an standard way to deal with environment variables. **METACALL** uses environment variables to define custom paths for plugins and scripts. - -- [`examples`](/source/examples) ... - -- [`filesystem`](/source/filesystem) provides an abstraction for operative system file system. - -- [`format`](/source/format) provides an standard way for printing to standard input output for old C versions that does not support newest constructions. - -- [`loader`](/source/loader) ... - -- [`loaders`](/source/loaders) - -- [`log`](/source/log) - -- [`memory`](/source/memory) - -- [`metacall`](/source/metacall) - -- [`ports`](/source/ports) - -- [`preprocessor`](/source/preprocessor) - -- [`reflect`](/source/reflect) - -- [`scripts`](/source/scripts) - -- [`serial`](/source/serial) - -- [`serials`](/source/serials) - -- [`tests`](/source/tests) - -- [`version`](/source/version) - -### 5.2 Reflect - -The module that holds the representation of types, values and functions is called [`reflect`](/source/reflect) and it handles the abstraction of code loaded into **METACALL**. - -**METACALL** uses reflection and introspection techniques to inspect the code loaded by the [`loaders`](/source/loaders) in order to interpret it and provide an higher abstraction of it. With this higher abstraction **METACALL** can easily inter-operate between languages transparently. - -#### 5.2.1 Type System - -**METACALL** implements an abstract type system which is a binary representation of the types supported by it. This means that **METACALL** can convert any type of a language to its own type system and back. Each loader is responsible of doing this conversions. - -**METACALL** maintains most of the types of the languages but not all are supported. If new types are added they have to be implemented in the [`reflect`](/source/reflect) module and also in the [`loaders`](/source/loaders) and [`serials`](/source/serials) to fully support it. - -| Type | Value | -|:-------:|--------------------------------------------------------------------| -| Boolean | `true` or `false` | -| Char | `-128` to `127` | -| Short | `-32,768` to `32,767` | -| Int | `-2,147,483,648` to `2,147,483,647` | -| Long | `–9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` | -| Float | `1.2E-38` to `3.4E+38` | -| Double | `2.3E-308` to `1.7E+308` | -| String | NULL terminated list of characters | -| Buffer | Blob of memory representing a binary data | -| Array | Arrangement of values of any type | -| Map | List of elements formed by a key (String) value (Any) pair (Array) | -| Pointer | Low level representation of a memory reference | -| Null | Representation of NULL value type | - -- Boolean is mostly represented by an integer value. There are languages that does not support it so it gets converted to a integer value in the memory layout. - -- Integer and Floating Point values provide a complete abstraction to numerical types. Type sizes are preserved and the correct type is used when using any number. This depends on the internal implementation of the value by the run-time. Although there can be problems related to this. A `bignum` type from Ruby may overflow if it is too big when trying to convert it to a `float` type in C#. - -- String is represented by ASCII encoding currently. Future versions will implement multiple encodings to be interoperable between other language encodings. - -- Buffer represents a blob of raw memory (i.e. an array of bytes). This can be used to represent files as images or any other resources into memory. - -- Array is implemented by means of array of values, which you can think it should be called _list_ instead. But as the memory layout is stored into a contiguous memory block of references to values, it is considered an array. - -- Map implements an associative key value pair container. A map is implemented with an array of two sized elements array. Each element of the map is an array of size two, where the first element of it is always an String and the second element is a value of any type. - -- Pointer is an opaque value representing a raw reference to a memory block. Some languages allow to use references to memory and some others not. This type is opaque because **METACALL** does not know what kind of concrete value represents it. The representation may be a complex type handled by the developer source code inside the run-time. - -- Null type implements a null value. This type has only been implemented in order to support null value from multiple run-times. It represents a null value and it does not have data size on the value allocated. - -#### 5.2.2 Values - -Values represent the instances of the **METACALL** type system. - -The memory layout guarantees to fit at least the same size of the types into memory. This means if a boolean type can be represented with one bit inside a value of one byte size, maybe this value is stored in a bigger memory block and this fact is architecture and platform dependant. - -When converting values between different types, if any potential number overflow or invalid conversion between types is done, **METACALL** will warn about it. If any conversion of types can be handled by **METACALL**, it will automatically cast or transform the values into the target type automatically in order to avoid errors in the call. - -The value model is implemented by means of object pool. Each value is a reference to a memory block allocated from a memory pool (which can be injected into **METACALL**). The references can be passed by value, this means **METACALL** copies the reference value instead of the data which this reference is pointing to, like most run-times do when managing their own values. - -Each created value must be destroyed manually. Otherwise it will lead to a memory leak. This fact only occurs when dealing with **METACALL** at C level. If **METACALL** is being used in an higher language through [`ports`](/source/ports), the developer does not have to care about memory management. - -The value memory layout is described in the following form. - -| Memory Offset | `0` to `sizeof(data) - 1` | `sizeof(data)` to `sizeof(data) + sizeof(type_id) - 1` | -|:-------------:|:-------------------------:|:------------------------------------------------------:| -| **Content** | **DATA** | **TYPE ID** | - -This layout is used by the following reasons. - -- Data is located at the first position of the memory block, so it can be used as a normal low level value. This allows to threat **METACALL** values as a normal C values. Therefore you can use **METACALL** with normal pointers to existing variables, literal values as shown in the previous examples or **METACALL** values. - -- Data can be accessed faster as it is located at first position of the memory block. There is not extra calculation of an offset when trying to access the pointer. - -- Data and type id are contiguously allocated in order to threat it as the same memory block so it can be freed with one operation. - -#### 5.2.3 Functions - -Functions are an abstract callable representation of functions, methods or procedures loaded by [`loaders`](/source/loaders). The functions are like a template who is linked to a loader run-time and allows to do a foreign function call. - -A function is composed by a name and a signature. The signature defines the arguments name, type, and return type if any. When a function is loaded, **METACALL** tries to inspect the signature and records the types if any. It stores the arguments name and size and also a concrete type that will be used later by the loader to implement the call to the run-time. - -The function interface must be implemented by the [`loaders`](/source/loaders) and it has the following form. - -``` c -typedef struct function_interface_type -{ - function_impl_interface_create create; - function_impl_interface_invoke invoke; - function_impl_interface_destroy destroy; - -} * function_interface; -``` - -- `create` instantiates the function concrete data related to the run-time. -- `invoke` transforms arguments from [`reflect`](/source/reflect) abstract types to run-time concrete types, executes the call in the run-time, and converts the result of the call from run-time concrete type to [`reflect`](/source/reflect) abstract type. -- `destroy` clears all data previously instantiated in `create`. - -The type deduction can be done at different levels. For example, it is possible to guess function types from the loaded code. - -``` python -def multiply_type(a: int, b: int) -> int: - return a * b -``` - -If this code is loaded, **METACALL** will be able to inspect the types and define the signature. Signature includes the names of the arguments, the types of those arguments if any, and the return type if any. - -It may be possible that the function loaded into **METACALL** is duck typed. This means it does not have information about what types it supports and therefore they cannot be inspected statically. - -``` python -def multiply_duck(a, b): - return a * b -``` - -At low level **METACALL** must always know the types to do the call. This types can be inferred statically or dynamically and this has implications over the call model. - -In the first example, we can simply call the function without specifying the types. - -``` c -metacall("multiply_type", 3, 4); // 12 -``` - -As the signature is already know the literal values `3` and `4` can be converted into **METACALL** values automatically. Note that in this case, as literal values are provided, if we pass a double floating point, the memory representation of the value will be corrupted as there is no possible way to detect input values and cast them to the correct target values. - -In the second example, the values are not know. If we use the same API to call the function, **METACALL** will not be able to call correctly the function as its types are not know. To allow calls to duck typed functions the developer must specify the value types he is passing to the function. - -``` c -const enum metacall_value_id multiply_types[] = -{ - METACALL_INT, METACALL_INT -}; +const { sum } = require('./sum.py'); -metacallt("multiply_duck", multiply_types, 3, 4); // 12 +sum(3, 4); // 7 ``` -This method allows to pass different value types to the same function. The following call would be valid too. - -``` c -const enum metacall_value_id multiply_types[] = -{ - METACALL_DOUBLE, METACALL_DOUBLE -}; - -metacallt("multiply_duck", multiply_types, 3.0, 4.0); // 12.0 -``` - -### 5.3 Plugins - -**METACALL** has a plugin architecture implemented at multiple levels. - -- Loaders implement a layer of plugins related to the run-times. - -- Serials implement a layer of (de)serializers in order to transform input (arguments) or output (return value) of the calls into a generic format. - -- Detours is another layer of plugins focused on low level function interception (hooks). - -Each plugin is a piece of software that can be dynamically loaded into the **METACALL** core, used and unloaded when it is not needed anymore. - -#### 5.3.1 Loaders - -Loaders are responsible for embedding run-times into **METACALL**. Each loader has the following interface. - -``` c -typedef struct loader_impl_interface_type -{ - loader_impl_interface_initialize initialize; - loader_impl_interface_execution_path execution_path; - loader_impl_interface_load_from_file load_from_file; - loader_impl_interface_load_from_memory load_from_memory; - loader_impl_interface_load_from_package load_from_package; - loader_impl_interface_clear clear; - loader_impl_interface_discover discover; - loader_impl_interface_destroy destroy; - -} * loader_impl_interface; -``` - -A loader must implement it to be considered a valid loader. - -- `initialize` starts up the run-time. -- `execution_path` defines a new import path to the run-time. -- `load_from_file` loads a code from file into the run-time and returns a handle which represents it. -- `load_from_memory` loads a code from memory into the run-time and returns a handle which represents it. -- `load_from_package` loads a code from a compiled library or package into the run-time and returns a handle which represents it. -- `clear` unloads a handle from the run-time. -- `discover` inspects a handle previously loaded. -- `destroy` shutdowns the run-time. - -##### 5.3.1.1 Python - -##### 5.3.1.2 NodeJS - -##### 5.3.1.3 JavaScript - -##### 5.3.1.4 C# # - -##### 5.3.1.5 Ruby - -##### 5.3.1.6 Mock - -##### 5.3.1.7 File - -#### 5.3.2 Serials - -##### 5.3.2.1 MetaCall - -##### 5.3.2.2 RapidJSON - -#### 5.3.3 Detours - -##### 5.3.3.1 FuncHook - -### 5.4 Ports - -### 5.5 Serialization - -### 5.6 Memory Layout - -### 5.7 Fork Model - -**METACALL** implements a fork safe model. This means if **METACALL** is running in any program instance, the process where is running can be forked safely at any moment of the execution. This fact has many implications at design, implementation and use level. But the whole **METACALL** architecture tries to remove all responsibility from the developer and make this transparent. - -To understand the **METACALL** fork model, first of all we have to understand the implications of the forking model in operative systems and the difference between [fork-one and fork-all models](https://docs.oracle.com/cd/E37838_01/html/E61057/gen-1.html). - -The main difference between fork-one and fork-all is that in fork-one only the thread which called the fork is preserved after the fork (i.e. gets cloned). In fork-all model, all threads are preserved after cloning. POSIX uses fork-one model, meanwhile Oracle Solaris use the fork-all model. - -Because of fork-one model, forking a running run-time like NodeJS (which has a thread pool) implies that in the child process the thread pool will be almost dead except the thread which did the fork call. So NodeJS run-time cannot continue the execution anymore and the event-loop enters into a deadlock state. - -When a fork is done, the status of the execution is lost by the moment. **METACALL** is not able to preserve the state when a fork is done. Some run-times do not allow to preserve the internal state. For example, the bad design of NodeJS does not allow to manage the thread pool from outside, so it cannot be preserved after a fork. - -Because of these restrictions, **METACALL** cannot preserve the status of the run-times. In the future this model will be improved to maintain consistency and preserve the execution state of the run-times making **METACALL** more robust. - -Although the state is not preserved, fork safety is. The mechanism **METACALL** uses to allow fork safety is described in the following enumeration. - -1) Intercept fork call done by the program where **METACALL** is running. - -2) Shutdown all run-times by means of unloading all loaders. - -3) Execute the real fork function. - -4) Restore all run-times by means of reloading all loaders. - -5) Execute user defined fork callback if any. - -To achieve this, **METACALL** hooks fork primitives depending on the platform. - -- `fork` on POSIX systems. -- `RtlCloneUserProcess` on Windows systems. - -If you use `clone` instead of `fork` to spawn a new process in a POSIX system, **METACALL** won't catch it. - -Whenever you call a to a cloning primitive **METACALL** intercepts it by means of [**`detour`**](/source/detour). Detours is a way to intercept functions at low level by editing the memory and introducing a jump over your own function preserving the address of the old one. **METACALL** uses this method instead of POSIX `pthread_atfork` for three main reasons. - -- The first one is that `pthread_atfork` is only supported by POSIX systems. So it is not a good solution because of the philosophy of **METACALL** is to be as cross-platform as possible. - -- The second is that `pthread_atfork` has a [bug in the design of the standard](https://stackoverflow.com/a/6605487). It was designed to solve a problem which cannot be solved with `pthread_atfork` itself. This means that even having the control of NodeJS thread pool, it will not be possible to restore the [mutexes](https://github.com/nodejs/node/blob/v8.x/src/node_platform.cc) in the child process. The only possibility is to re-implement the thread pool of NodeJS with async safe primitives like a semaphore. Async safe primitives will be able to work in the child process handler. But this is not possible as it enters in conflict with the design decision of to not modify the run-times. - -- The third one is that the mechanism of `pthread_atfork` also [will be deprecated](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html) because of second reason. - > The `pthread_atfork()` function may be formally deprecated (for example, by shading it OB) in a future version of this standard. - -Detours model is not safe. It is platform dependant and implies that the program modifies the memory of itself during the execution which is not safe at all and can induce bugs or security flaws if it is not done correctly. But because of limitations of run-times, there is not another alternative to solve the problem of fork safety. - -Usually the developer is the same who does the fork, but it may be possible that **METACALL** is embedded into a larger application and the developer is in the middle between the application code and **METACALL** so it is impossible to control when a fork is done. Because of this the developer can register a callback by means of [**`metacall_fork`**](/source/metacall/include/metacall/metacall_fork.h) to know when a fork is executed to do the actions needed after the fork, for example, re-loading all previous code and restore the state of the run-times. This gives a partial solution to the problem of losing the state when doing a fork. - -### 5.8 Threading Model - -## 5. Application Programming Interface (API) - -## 6. Build System - -Follow these steps to build and install **METACALL** manually. - -``` sh -git clone --recursive https://github.com/metacall/core.git -mkdir core/build && cd core/build -cmake .. -make -make test -sudo make install -``` - -### 6.1 Build Options - -These options can be set using **`-D`** prefix when configuring CMake. For example, the following configuration enables the build of Python and Ruby loaders. - +`shell` ``` sh -cmake -DOPTION_BUILD_LOADERS_PY=On -DOPTION_BUILD_LOADERS_RB=On .. +metacall main.js ``` -Available build options are the following ones. +**MetaCall** is an extensible, embeddable, and interoperable cross-platform polyglot runtime. It supports NodeJS, Vanilla JavaScript, TypeScript, Python, Ruby, C#, Java, WASM, Go, C, C++, Rust, D, Cobol [and more](https://github.com/metacall/core/blob/develop/docs/README.md#2-language-support). -| Build Option | Description | Default Value | -|:---------------------------:|--------------------------------------------------------|:-------------:| -| **BUILD_SHARED_LIBS** | Build shared instead of static libraries. | ON | -| **OPTION_BUILD_DIST_LIBS** | Build all libraries into a single compilation unit. | ON | -| **OPTION_SELF_CONTAINED** | Create a self-contained install with all dependencies. | OFF | -| **OPTION_BUILD_TESTS** | Build tests. | ON | -| **OPTION_BUILD_BENCHMARKS** | Build benchmarks. | OFF | -| **OPTION_BUILD_DOCS** | Build documentation. | OFF | -| **OPTION_BUILD_EXAMPLES** | Build examples. | ON | -| **OPTION_BUILD_LOADERS** | Build loaders. | ON | -| **OPTION_BUILD_SCRIPTS** | Build scripts. | ON | -| **OPTION_BUILD_SERIALS** | Build serials. | ON | -| **OPTION_BUILD_DETOURS** | Build detours. | ON | -| **OPTION_BUILD_PORTS** | Build ports. | OFF | -| **OPTION_FORK_SAFE** | Enable fork safety. | OFF | -| **OPTION_THREAD_SAFE** | Enable thread safety. | OFF | -| **OPTION_COVERAGE** | Enable coverage. | OFF | -| **CMAKE_BUILD_TYPE** | Define the type of build. | Release | +## Install -It is possible to enable or disable concrete loaders, script, ports, serials or detours. For building use the following options. - -| Build Option Prefix | Build Option Suffix | -|:-------------------------:|-----------------------------------------------------------------------| -| **OPTION_BUILD_LOADERS_** | `C` `JS` `CS` `MOCK` `PY` `JSM` `NODE` `RB` `JSM` `FILE` | -| **OPTION_BUILD_SCRIPTS_** | `C` `CS` `JS` `NODE` `PY` `RB` `JAVA` | -| **OPTION_BUILD_SERIALS_** | `METACALL` `RAPID_JSON` | -| **OPTION_BUILD_DETOURS_** | `FUNCHOOK` | -| **OPTION_BUILD_PORTS_** | `CS` `CXX` `D` `GO` `JAVA` `JS` `LUA` `NODE` `PHP` `PL` `PY` `R` `RB` | - -### 6.2 Coverage - -In order to run code coverage and obtain html reports use the following commands. Note, test must be run before executing code coverage. - -``` sh -make -make test -make -k gcov -make -k lcov -make -k lcov-genhtml -``` - -The output reports will be generated in `${CMAKE_BINARY_DIR}/lcov/html/selected_targets` in html format. - -To obtain a report of a single `target` do: - -``` sh -make -make test -make -gcov -make -geninfo -make -genhtml -``` - -## 7. Platform Support - -The following platforms and architectures have been tested an work correctly with all plugins of **METACALL**. - -| Operative System | Architecture | Compiler | Build Status | -|:-------------------------:|:-------------------:|:---------------:|:------------------------------------------------------------------------------------------------------:| -| **`ubuntu:xenial`** | **`amd64`** | **`gcc`** | | -| **`debian:stretch-slim`** | **`amd64`** | **`gcc:6.3.0`** | [![build](https://gitlab.com/metacall/core/badges/master/build.svg)](https://gitlab.com/metacall/core) | -| **`debian:buster-slim`** | **`amd64`** | **`gcc:8.2.0`** | | -| **`windows`** | **`x86`** **`x64`** | **`msvc`** | | - -### 7.1 Docker Support - -To provide a reproducible environment **METACALL** is also distributed under Docker on [DockerHub](https://hub.docker.com/r/metacall/core). Current images are based on `debian:stretch-slim` for `amd64` architecture. - -For pulling the **METACALL** `latest` image containing the runtime, use: - -``` sh -docker pull metacall/core -``` - -For pulling a specific image depending on the tag, use: - -- **METACALL** `deps` image. Includes all dependencies for development: - -``` sh -docker pull metacall/core:deps -``` - -- **METACALL** `dev` image. Includes all dependencies, headers and libraries for development: - -``` sh -docker pull metacall/core:dev -``` - -- **METACALL** `runtime` image. Includes all dependencies and libraries for runtime: +The easiest way to install **MetaCall** is the following: ``` sh -docker pull metacall/core:runtime +curl -sL https://raw.githubusercontent.com/metacall/install/master/install.sh | sh ``` -- **METACALL** `cli` image. Includes all dependencies and libraries for runtime and the CLI as entry point (equivalent to `latest`): - -``` sh -docker pull metacall/core:cli -``` - -### 7.1.1 Docker Development - -It is possible to develop **METACALL** itself or applications using **METACALL** as standalone library with Docker. The `dev` image can be used for development. It contains all dependencies with all run-times installed with the code, allowing debugging too. - -Use the following commands to start developing with **METACALL**: - -``` sh -mkdir -p $HOME/metacall -code $HOME/metacall -``` - -We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. - -``` sh -docker pull metacall/core:dev -docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:dev /bin/bash -``` - -Inside docker terminal you can run `python` or `ruby` command to test what you are developing. You can also run `metacallcli` to test (load, clear, inspect and call). - -### 7.1.2 Docker Testing - -An alternative for testing is to use a reduced image that includes the runtime and also the CLI. This alternative allows fast prototyping and CLI management in order to test and inspect your own scripts. - -Use the following commands to start testing with **METACALL**: - -``` sh -mkdir -p $HOME/metacall -code $HOME/metacall -``` - -We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. - -``` sh -docker pull metacall/core:cli -docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:cli -``` - -After the container is up, it is possible to load any script contained in host folder `$HOME/metacall`. If we have a `script.js` inside the folder, we can just load it (each line beginning with `>` is the input command): - -`script.js` -``` js -function sum(left, right) { - return left + right; -} - -module.exports = { - sum -}; -``` - -`Command Line Interface` -``` sh -> load node script.js -Script (script.js) loaded correctly -> inspect -runtime node { - module script { - function sum(left, right) - } -} -runtime __metacall_host__ -> call sum(3, 5) -8.0 -> exit -``` - -Where `script.js` is a script contained in host folder `$HOME/metacall` that will be loaded on the CLI after starting up the container. Type `help` to see all available CLI commands. - -## 8. License +For more information about other install methodologies and platforms or Docker, check the [install documentation](https://github.com/metacall/core/blob/develop/docs/README.md#41-installation). -**METACALL** is licensed under **[Apache License Version 2.0](/LICENSE)**. +## Examples ->Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia <> -> ->Licensed under the Apache License, Version 2.0 (the "License"); ->you may not use this file except in compliance with the License. ->You may obtain a copy of the License at -> -> http://www.apache.org/licenses/LICENSE-2.0 -> ->Unless required by applicable law or agreed to in writing, software ->distributed under the License is distributed on an "AS IS" BASIS, ->WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ->See the License for the specific language governing permissions and ->limitations under the License. +You can find a complete [list of examples in the documentation](https://github.com/metacall/core/blob/develop/docs/README.md#43-examples). If you are interested in submitting new examples, please [contact us in our chats](#badges). diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..6f16bd3257 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.9.15 \ No newline at end of file diff --git a/cmake/CMakeDebug.cmake b/cmake/CMakeDebug.cmake index 3feadae2ea..dd80a221b9 100644 --- a/cmake/CMakeDebug.cmake +++ b/cmake/CMakeDebug.cmake @@ -2,7 +2,7 @@ # CMake Debug Utilities by Parra Studios # CMake debugging utilities and inspection facilities. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/CheckCCompilerFlagStackSmashing.c b/cmake/CheckCCompilerFlagStackSmashing.c index 4e303640d7..2a018d92e6 100644 --- a/cmake/CheckCCompilerFlagStackSmashing.c +++ b/cmake/CheckCCompilerFlagStackSmashing.c @@ -1,4 +1,4 @@ -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { char buffer[2]; diff --git a/cmake/CheckCCompilerFlagStackSmashing.cmake b/cmake/CheckCCompilerFlagStackSmashing.cmake index 3537749868..ea31ed5d77 100644 --- a/cmake/CheckCCompilerFlagStackSmashing.cmake +++ b/cmake/CheckCCompilerFlagStackSmashing.cmake @@ -2,7 +2,7 @@ # Compiler checker for stack smashing flags by Parra Studios # Tests if a defined stack smashing security flag is available. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/CheckCXXCompilerFlagStackSmashing.cmake b/cmake/CheckCXXCompilerFlagStackSmashing.cmake index 1d85e307d8..9fa7f95891 100644 --- a/cmake/CheckCXXCompilerFlagStackSmashing.cmake +++ b/cmake/CheckCXXCompilerFlagStackSmashing.cmake @@ -2,7 +2,7 @@ # Compiler checker for stack smashing flags by Parra Studios # Tests if a defined stack smashing security flag is available. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/CheckCXXCompilerFlagStackSmashing.cpp b/cmake/CheckCXXCompilerFlagStackSmashing.cpp index 4e303640d7..2a018d92e6 100644 --- a/cmake/CheckCXXCompilerFlagStackSmashing.cpp +++ b/cmake/CheckCXXCompilerFlagStackSmashing.cpp @@ -1,4 +1,4 @@ -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { char buffer[2]; diff --git a/cmake/ClangDevTools.cmake b/cmake/ClangDevTools.cmake new file mode 100644 index 0000000000..3013037744 --- /dev/null +++ b/cmake/ClangDevTools.cmake @@ -0,0 +1,37 @@ +# See FindClangFormat.cmake +# Variables of interest on this file: ${ClangFormat_VERSION} and ${ClangFormat_EXECUTABLE} + +# Get only C/C++ files for now +file(GLOB_RECURSE + ALL_SOURCE_FILES + LIST_DIRECTORIES OFF + FOLLOW_SYMLINKS + ${CMAKE_SOURCE_DIR}/source/**/*.cpp + ${CMAKE_SOURCE_DIR}/source/**/*.hpp + ${CMAKE_SOURCE_DIR}/source/**/*.h + ${CMAKE_SOURCE_DIR}/source/**/*.c + ${CMAKE_SOURCE_DIR}/source/**/*.cc + ${CMAKE_SOURCE_DIR}/source/**/*.hh + ${CMAKE_SOURCE_DIR}/source/**/*.cxx + ${CMAKE_SOURCE_DIR}/source/**/*.inl +) + +# clang-tidy not implemented yet +#add_custom_target( +# clang-tidy +# COMMAND /usr/bin/clang-tidy +# ${ALL_SOURCE_FILES} +# -config='' +# -- +# -std=c++11 +# ${INCLUDE_DIRECTORIES} +#) + +add_custom_target( + clang-format + COMMAND ${ClangFormat_EXECUTABLE} + --verbose + -style=file + -i + ${ALL_SOURCE_FILES} +) diff --git a/cmake/CompileOptions.cmake b/cmake/CompileOptions.cmake index 4693702de5..b3cd23a466 100644 --- a/cmake/CompileOptions.cmake +++ b/cmake/CompileOptions.cmake @@ -2,7 +2,13 @@ # Compile options configuration # -option(OPTION_BUILD_SANITIZER "Build with sanitizer compiler options." OFF) +option(OPTION_BUILD_ADDRESS_SANITIZER "Build with sanitizer compiler options." OFF) +option(OPTION_BUILD_THREAD_SANITIZER "Build with thread sanitizer compiler options." OFF) +option(OPTION_BUILD_MEMORY_SANITIZER "Build with memory sanitizer compiler options." OFF) + +if((OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_MEMORY_SANITIZER) OR (OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_THREAD_SANITIZER) OR (OPTION_BUILD_MEMORY_SANITIZER AND OPTION_BUILD_THREAD_SANITIZER)) + message(FATAL_ERROR "OPTION_BUILD_ADDRESS_SANITIZER and OPTION_BUILD_MEMORY_SANITIZER and OPTION_BUILD_THREAD_SANITIZER are mutually exclusive, choose one of them") +endif() # # Platform and architecture setup @@ -13,6 +19,7 @@ string(TOUPPER ${CMAKE_SYSTEM_NAME} SYSTEM_NAME_UPPER) # Determine architecture (32/64 bit) set(X64 OFF) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(X64 ON) endif() @@ -27,21 +34,30 @@ include(Portability) # Project options # +# Test for GNU 4.9+, Clang 3.6+ or ((Visual Studio C++ or Clang with MSVC backend) and Visual Studio 2022 or superior) +if(("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND ${CMAKE_C_COMPILER_VERSION} VERSION_GREATER 4.9) OR + ((("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") OR + ("${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")) AND ${CMAKE_C_COMPILER_VERSION} VERSION_GREATER 3.6) OR + ( + (("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") OR + (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") AND ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC"))) AND + (MSVC_VERSION GREATER_EQUAL 1930) + ) +) + set(C_STANDARD 11) +else() + set(C_STANDARD 99) # TODO: Implement support for older standards +endif() + set(DEFAULT_PROJECT_OPTIONS DEBUG_POSTFIX "d" - CXX_STANDARD 11 # Not available before CMake 3.1; see below for manual command line argument addition + CXX_STANDARD 11 LINKER_LANGUAGE "CXX" POSITION_INDEPENDENT_CODE ON CXX_VISIBILITY_PRESET "hidden" + C_STANDARD ${C_STANDARD} ) -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set(DEFAULT_PROJECT_OPTIONS - ${DEFAULT_PROJECT_OPTIONS} - C_STANDARD 99 # TODO: Provide preprocessor support for older standards (GCC) - ) -endif() - # # Include directories # @@ -52,27 +68,206 @@ set(DEFAULT_INCLUDE_DIRECTORIES) # Libraries # -if(OPTION_BUILD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - set(DEFAULT_LIBRARIES -lasan -lubsan) +# Valgrind +if(OPTION_TEST_MEMORYCHECK) + set(MEMORYCHECK_COMPILE_DEFINITIONS + "__MEMORYCHECK__=1" + ) + + set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full") + # set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --show-leak-kinds=all") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --trace-children=yes") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --show-reachable=yes") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --track-origins=yes") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --num-callers=100") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --smc-check=all-non-file") # for JITs + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-dl.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-python.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-node.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-wasm.supp") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${CMAKE_SOURCE_DIR}/source/tests/memcheck/valgrind-wasm.supp") + + # TODO: Implement automatic detection for valgrind suppressions and create a proper test suite for the CI + set(MEMORYCHECK_ADDITIONAL_SUPPRESSIONS + "/usr/lib/valgrind/python3.supp" + "/usr/lib/valgrind/debian.supp" + ) + + foreach(SUPPRESSION ${MEMORYCHECK_ADDITIONAL_SUPPRESSIONS}) + if(EXISTS "${SUPPRESSION}") + set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --suppressions=${SUPPRESSION}") + endif() + endforeach() + + # This is needed in order to allow valgrind to properly track malloc in Python + set(TESTS_MEMCHECK_ENVIRONMENT_VARIABLES + "PYTHONMALLOC=malloc" + ) else() - set(DEFAULT_LIBRARIES) + set(MEMORYCHECK_COMPILE_DEFINITIONS) + set(MEMORYCHECK_COMMAND_OPTIONS) + set(TESTS_MEMCHECK_ENVIRONMENT_VARIABLES) endif() +# ThreadSanitizer is incompatible with AddressSanitizer and LeakSanitizer +if(OPTION_BUILD_THREAD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + set(SANITIZER_LIBRARIES -ltsan) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES + "TSAN_OPTIONS=suppressions=${CMAKE_SOURCE_DIR}/source/tests/sanitizer/tsan.supp" + ) + set(SANITIZER_COMPILE_DEFINITIONS + "__THREAD_SANITIZER__=1" + ) +elseif(OPTION_BUILD_MEMORY_SANITIZER AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + # TODO: This requires much more effort than expected: https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo + set(SANITIZER_LIBRARIES) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES) + set(SANITIZER_COMPILE_DEFINITIONS + "__MEMORY_SANITIZER__=1" + ) +elseif(OPTION_BUILD_ADDRESS_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + set(SANITIZER_LIBRARIES -lasan -lubsan) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES + "LSAN_OPTIONS=verbosity=1:log_threads=1:print_suppressions=false:suppressions=${CMAKE_SOURCE_DIR}/source/tests/sanitizer/lsan.supp" + + # Specify handle_segv=0 and detect_leaks=0 for the JVM (https://blog.gypsyengineer.com/en/security/running-java-with-addresssanitizer.html) + # "ASAN_OPTIONS=handle_segv=0:symbolize=1:alloc_dealloc_mismatch=0:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:fast_unwind_on_malloc=0" + + # TODO: We should document each flag why is it used, because now we do not know what runtime has each requirement and why. + # Another option should be to separate by runtimes and only set up them on the ASAN tests that require them, + # because we do not need to disable all features on all tests, this may hide bugs in the core library for example. + + # Specify use_sigaltstack=0 as CoreCLR uses own alternate stack for signal handlers (https://github.com/swgillespie/coreclr/commit/bec020aa466d08e49e007d0011b0e79f8f7c7a62) + "ASAN_OPTIONS=use_sigaltstack=0:symbolize=1:alloc_dealloc_mismatch=0:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:fast_unwind_on_malloc=0" + ) + set(SANITIZER_COMPILE_DEFINITIONS + "__ADDRESS_SANITIZER__=1" + ) +else() + set(SANITIZER_LIBRARIES) + set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES) + set(SANITIZER_COMPILE_DEFINITIONS) +endif() + +function(find_sanitizer NAME LINK_OPTION) + string(TOUPPER "${NAME}" NAME_UPPER) + set(SANITIZER_PROGRAM_CODE "int main() {return 0;}") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate.cpp" "${SANITIZER_PROGRAM_CODE}") + + try_compile( + STATUS + ${PROJECT_OUTPUT_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate.cpp + OUTPUT_VARIABLE SANITIZER_COMPILER_OUTPUT + LINK_OPTIONS ${LINK_OPTION} + COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate + ) + + if(NOT STATUS) + message(FATAL_ERROR "Could not find location for lib${NAME}: ${SANITIZER_COMPILER_OUTPUT}") + return() + endif() + + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES ${CMAKE_CURRENT_BINARY_DIR}/sanitizer_locate + RESOLVED_DEPENDENCIES_VAR SANITIZER_PROGRAM_LIBRARIES + ) + + foreach(DEPENDENCY IN LISTS SANITIZER_PROGRAM_LIBRARIES) + string(FIND "${DEPENDENCY}" "${NAME}" POSITION) + if(POSITION GREATER -1) + set(LIB${NAME_UPPER}_PATH "${DEPENDENCY}" PARENT_SCOPE) + return() + endif() + endforeach() +endfunction() + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + if(OPTION_BUILD_THREAD_SANITIZER) + find_sanitizer(tsan -fsanitize=thread) + set(SANITIZER_LIBRARIES_PATH + "${LIBTSAN_PATH}" + ) + elseif(OPTION_BUILD_MEMORY_SANITIZER) + set(SANITIZER_LIBRARIES_PATH) # TODO + elseif(OPTION_BUILD_ADDRESS_SANITIZER) + find_sanitizer(asan -fsanitize=address) + find_sanitizer(ubsan -fsanitize=undefined) + set(SANITIZER_LIBRARIES_PATH + "${LIBASAN_PATH}" + "${LIBUBSAN_PATH}" + ) + endif() +endif() + +if(SANITIZER_LIBRARIES_PATH) + if(PROJECT_OS_LINUX) + list(JOIN SANITIZER_LIBRARIES_PATH " " SANITIZER_LIBRARIES_PATH_JOINED) + set(TESTS_SANITIZER_PRELOAD_ENVIRONMENT_VARIABLES + "LD_PRELOAD=${SANITIZER_LIBRARIES_PATH_JOINED}" + ) + elseif(PROJECT_OS_FAMILY MATCHES "macos") + list(JOIN SANITIZER_LIBRARIES_PATH ":" SANITIZER_LIBRARIES_PATH_JOINED) + set(TESTS_SANITIZER_PRELOAD_ENVIRONMENT_VARIABLES + "DYLD_INSERT_LIBRARIES=${SANITIZER_LIBRARIES_PATH_JOINED}" + "DYLD_FORCE_FLAT_NAMESPACE=1" + ) + endif() +endif() + +if((PROJECT_OS_WIN AND MSVC) OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + # MSVC and Clang do not require to link manually the sanitizer libraries + set(SANITIZER_LIBRARIES) +endif() + +# Set default libraries +set(DEFAULT_LIBRARIES + ${SANITIZER_LIBRARIES} +) + # # Compile definitions # +if(OPTION_BUILD_LOG_PRETTY) + set(LOG_POLICY_FORMAT_PRETTY_VALUE 1) +else() + set(LOG_POLICY_FORMAT_PRETTY_VALUE 0) +endif() + +if(OPTION_MEMORY_TRACKER) + set(REFLECT_MEMORY_TRACKER_VALUE 1) +else() + set(REFLECT_MEMORY_TRACKER_VALUE 0) +endif() + set(DEFAULT_COMPILE_DEFINITIONS + LOG_POLICY_FORMAT_PRETTY=${LOG_POLICY_FORMAT_PRETTY_VALUE} + REFLECT_MEMORY_TRACKER=${REFLECT_MEMORY_TRACKER_VALUE} SYSTEM_${SYSTEM_NAME_UPPER} + ${MEMORYCHECK_COMPILE_DEFINITIONS} + ${SANITIZER_COMPILE_DEFINITIONS} ) # MSVC compiler options -if(WIN32) +if(PROJECT_OS_WIN AND MSVC) set(DEFAULT_COMPILE_DEFINITIONS ${DEFAULT_COMPILE_DEFINITIONS} _SCL_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the Standard C++ Library _CRT_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the CRT Library ) -endif () +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEFAULT_COMPILE_DEFINITIONS + ${DEFAULT_COMPILE_DEFINITIONS} + DEBUG + ) +else() + set(DEFAULT_COMPILE_DEFINITIONS + ${DEFAULT_COMPILE_DEFINITIONS} + NDEBUG + ) +endif() # # Compile options @@ -81,80 +276,219 @@ endif () # They are empty by default set(DEFAULT_COMPILE_OPTIONS) -if(WIN32) +if(WIN32 AND MSVC) + # Build runtime as multithreaded shared library + # if(${CMAKE_VERSION} VERSION_LESS "3.15") + # set(COMPILER_FLAGS_ID + # CMAKE_CXX_FLAGS + # CMAKE_CXX_FLAGS_DEBUG + # CMAKE_CXX_FLAGS_RELEASE + # CMAKE_C_FLAGS + # CMAKE_C_FLAGS_DEBUG + # CMAKE_C_FLAGS_RELEASE + # ) + # foreach(FLAG_ID ${COMPILER_FLAGS_ID}) + # string(REPLACE "/MD" "/MT" ${FLAG_ID} "${${FLAG_ID}}") + # endforeach() + # else() + # set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + # endif() + add_compile_options(/nologo) # Suppress Startup Banner - add_compile_options(/W4) # Set warning level to 4 - add_compile_options(/WX-) # Do not treat warnings as errors add_compile_options(/Gm-) # Disable minimal rebuild add_compile_options(/MP) # Build with Multiple Processes (number of processes equal to the number of processors) #add_compile_options(/wd4251 /wd4592) #add_compile_options(/ZH:SHA_256) # use SHA256 for generating hashes of compiler processed source files. - # Release if(CMAKE_BUILD_TYPE STREQUAL "Debug") - # TODO: Review debug optimization - #add_compile_options(/GL) # Enable debugging information - ##add_compile_options(/LTCG) # Enable debugging information + # Disable optimizations + add_compile_options(/Od) else() - add_compile_options(/GS) # Buffer Security Check - add_compile_options(/GF) # Enable read-only string pooling - #add_compile_options(/GW) # Enable read-only string pooling + # Enable read-only string pooling + add_compile_options(/GF) + + # Buffer Security Check + add_compile_options(/GS) + + # Disable Run-Time Error Checks + add_compile_options(/GR-) + + # Enable optimizations + # add_compile_options(/O2) # TODO: Enable when runtime checks can be disabled properly + add_compile_options(/Oi) + add_compile_options(/Oy) + + # TODO: Disable runtime checks (not compatible with O2) + # foreach(COMPILER_FLAGS + # CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE + # CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO + # CMAKE_C_FLAGS CMAKE_C_FLAGS_RELEASE + # CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + # ) + # string(REGEX REPLACE "/RTC[^ ]*" "" ${COMPILER_FLAGS} "${${COMPILER_FLAGS}}") + # endforeach(COMPILER_FLAGS) + + if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + # Enable debug symbols + add_compile_options(/Z7) + endif() + endif() + + # Sanitizers + if(OPTION_BUILD_THREAD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(/fsanitize=thread) + add_link_options(/INCREMENTAL:NO) + elseif(OPTION_BUILD_ADDRESS_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(/fsanitize=address) + # add_compile_options(/fsanitize=undefined) + add_link_options(/INCREMENTAL:NO) + elseif(OPTION_BUILD_MEMORY_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(/fsanitize=memory) + add_compile_options(/fsanitize=leak) + add_link_options(/INCREMENTAL:NO) endif() + endif() -if (PROJECT_OS_FAMILY MATCHES "unix") +if (PROJECT_OS_FAMILY MATCHES "unix" OR PROJECT_OS_FAMILY MATCHES "macos") - if(APPLE) + if(PROJECT_OS_FAMILY MATCHES "macos") # We cannot enable "stack-protector-strong" On OS X due to a bug in clang compiler (current version 7.0.2) - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_options(-fstack-protector) - endif() # Enable threads in OS X add_compile_options(-pthread) # clang options only add_compile_options(-Wreturn-stack-address) - else() - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_options(-fstack-protector-strong) - endif() endif() - if(PROJECT_OS_LINUX) - # Enable threads in linux - add_compile_options(-pthread) + if(PROJECT_OS_HAIKU) + add_compile_options(-fPIC) endif() # All warnings that are not explicitly disabled are reported as errors #add_compile_options(-Werror) + add_compile_options(-Wall) + add_compile_options(-Wextra) # Sanitizers - if(OPTION_BUILD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) - add_compile_options(-fuse-ld=gold) + if(OPTION_BUILD_THREAD_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(-fno-omit-frame-pointer) + add_compile_options(-fno-optimize-sibling-calls) + add_compile_options(-fsanitize=thread) + if(PROJECT_OS_FAMILY MATCHES "macos" OR (PROJECT_OS_FAMILY MATCHES "unix" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + add_link_options(-fsanitize=thread) + endif() + elseif(OPTION_BUILD_ADDRESS_SANITIZER AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) add_compile_options(-fno-omit-frame-pointer) add_compile_options(-fno-optimize-sibling-calls) add_compile_options(-fsanitize=undefined) add_compile_options(-fsanitize=address) - add_compile_options(-fsanitize=leak) - #add_compile_options(-fsanitize=thread) - - if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - add_compile_options(-fsanitize=memory) - add_compile_options(-fsanitize=memory-track-origins) + add_compile_options(-fsanitize-address-use-after-scope) + add_compile_options(-fsanitize=float-divide-by-zero) + add_compile_options(-fsanitize=float-cast-overflow) + if(PROJECT_OS_FAMILY MATCHES "unix") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-fsanitize=pointer-compare) + add_compile_options(-fsanitize=pointer-subtract) + add_compile_options(-fuse-ld=gold) + endif() + add_compile_options(-fsanitize=leak) + endif() + if(PROJECT_OS_FAMILY MATCHES "macos" OR (PROJECT_OS_FAMILY MATCHES "unix" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + add_link_options(-fsanitize=undefined) + add_link_options(-fsanitize=address) + add_link_options(-fsanitize-address-use-after-scope) endif() + elseif(OPTION_BUILD_MEMORY_SANITIZER AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + add_compile_options(-fno-omit-frame-pointer) + add_compile_options(-fno-optimize-sibling-calls) + add_compile_options(-fsanitize=undefined) + add_compile_options(-fsanitize=memory) + add_compile_options(-fsanitize-memory-track-origins) + add_compile_options(-fsanitize-memory-use-after-dtor) + if(PROJECT_OS_FAMILY MATCHES "macos") + add_link_options(-fsanitize=undefined) + add_link_options(-fsanitize=memory) + add_link_options(-fsanitize-memory-track-origins) + add_link_options(-fsanitize-memory-use-after-dtor) + endif() + endif() + + # Debug symbols + if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_compile_options(-g) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic") + endif() + + # Optimizations + if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_compile_options(-O3) endif() endif() +macro(check_symbol_executable symbol binary_path result_var) + if(WIN32) + find_program(DUMPBIN_EXECUTABLE dumpbin) + if(NOT DUMPBIN_EXECUTABLE) + message(FATAL_ERROR "Trying to find symbol ${symbol} in ${binary_path} but dumpbin was not found") + endif() + execute_process( + COMMAND ${DUMPBIN_EXECUTABLE} /symbols ${binary_path} + OUTPUT_VARIABLE dumpbin_output + RESULT_VARIABLE dumpbin_result + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(FIND "${dumpbin_output}" symbol SYMBOL_FOUND) + if(NOT SYMBOL_FOUND EQUAL -1) + set(${result_var} TRUE PARENT_SCOPE) + else() + set(${result_var} FALSE PARENT_SCOPE) + endif() + else() + find_program(NM_EXECUTABLE nm) + if(NM_EXECUTABLE) + execute_process( + COMMAND ${NM_EXECUTABLE} -D ${binary_path} + OUTPUT_VARIABLE nm_output + RESULT_VARIABLE nm_result + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(FIND "${nm_output}" symbol SYMBOL_FOUND) + if(NOT SYMBOL_FOUND EQUAL -1) + set(${result_var} TRUE PARENT_SCOPE) + else() + set(${result_var} FALSE PARENT_SCOPE) + endif() + else() + message(FATAL_ERROR "Trying to find symbol ${symbol} in ${binary_path} but nm was not found") + endif() + endif() +endmacro() + +function(check_asan_executable binary_path result_var) + check_symbol_executable("__asan_init" "${binary_path}" ${result_var}) +endfunction() + +function(check_tsan_executable binary_path result_var) + check_symbol_executable("__tsan_init" "${binary_path}" ${result_var}) +endfunction() + # # Linker options # set(DEFAULT_LINKER_OPTIONS) -# Use pthreads on mingw and linux -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_SYSTEM_NAME}" MATCHES "Linux") +if(PROJECT_OS_FAMILY MATCHES "macos" OR PROJECT_OS_LINUX OR PROJECT_OS_MINGW) + # Enable threads in linux, macos and mingw set(DEFAULT_LINKER_OPTIONS -pthread ) +elseif(PROJECT_OS_HAIKU) + set(DEFAULT_LINKER_OPTIONS + -lpthread + ) endif() diff --git a/cmake/Coverage.cmake b/cmake/Coverage.cmake new file mode 100644 index 0000000000..4a15b0efcb --- /dev/null +++ b/cmake/Coverage.cmake @@ -0,0 +1,48 @@ +# +# Coverage CMake support by Parra Studios +# Cross-compiler code coverage utility. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(NOT OPTION_COVERAGE) + return() +endif() + +include(CTest) + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") +endif() + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") +endif() + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage -fprofile-instr-generate -fcoverage-mapping") +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -fno-elide-constructors -fprofile-instr-generate -fcoverage-mapping") +endif() diff --git a/cmake/Distributable.cmake b/cmake/Distributable.cmake deleted file mode 100644 index 81a25c56b2..0000000000 --- a/cmake/Distributable.cmake +++ /dev/null @@ -1,84 +0,0 @@ -# -# CMake Distributable (Unity Build) library by Parra Studios -# CMake script to generate distributable (unity build) libraries. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -function(distributable_generate target_root unity_build_file) - - set(target_list "${ARGN}") - - set(unity_build_source) - - foreach(target ${target_list}) - - # Get target source files - get_target_property(target_sources - ${META_PROJECT_NAME}::${target} - SOURCES - ) - - # Add private linkage for all targets except metacall - if(NOT "${target}" STREQUAL "${target_root}") - string(TOUPPER ${target} target_upper) - set(unity_build_source "${unity_build_source}\n\#ifndef ${target_upper}_API") - - if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") - set(unity_build_source "${unity_build_source}\n\# define ${target_upper}_API __attribute__ ((visibility(\"hidden\")))") - else() - set(unity_build_source "${unity_build_source}\n\# define ${target_upper}_API ${target_upper}_NO_EXPORT") - add_definitions("-D${target_upper}_API=${target_upper}_NO_EXPORT") - endif() - - set(unity_build_source "${unity_build_source}\n\#endif /* ${target_upper}_API */") - else() - set(unity_build_source "${unity_build_source}\n\#ifndef ${target}_EXPORTS") - set(unity_build_source "${unity_build_source}\n\# define ${target}_EXPORTS") - set(unity_build_source "${unity_build_source}\n\#endif /* ${target}_EXPORTS */") - - add_definitions("-D${target}_EXPORTS") - endif() - - # Add include paths - include_directories("${CMAKE_BINARY_DIR}/source/${target}/include") - include_directories("${CMAKE_SOURCE_DIR}/source/${target}/include") - - # Write auto-generated includes into unity build - set(unity_build_source "${unity_build_source}\n\#include <${CMAKE_BINARY_DIR}/source/${target}/include/${target}/${target}_api.h>") - set(unity_build_source "${unity_build_source}\n\#include <${CMAKE_BINARY_DIR}/source/${target}/include/${target}/${target}_features.h>") - - # Write all includes into unity build - foreach(source ${target_sources}) - set(unity_build_source "${unity_build_source}\n\#include <${source}>") - endforeach() - - endforeach() - - set(unity_build_source "${unity_build_source}\n") - - if(EXISTS "${unity_build_file}") - file(READ "${unity_build_file}" unity_build_source_old) - - if("${unity_build_source_old}" STREQUAL "${unity_build_source}") - return() - endif() - endif() - - file(WRITE ${unity_build_file} "${unity_build_source}") - - message(STATUS "Unity build written in ${unity_build_file}") - -endfunction() diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake deleted file mode 100644 index 33d55b626f..0000000000 --- a/cmake/FindClang.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Find Clang -# -# It defines the following variables -# CLANG_FOUND - True if Clang found. -# CLANG_INCLUDE_DIRS - where to find Clang include files -# CLANG_LIBS - list of clang libs - -if(NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) - message(FATAL_ERROR "No LLVM and Clang support requires LLVM") - return() -endif(NOT LLVM_INCLUDE_DIRS OR NOT LLVM_LIBRARY_DIRS) - -macro(FIND_AND_ADD_CLANG_LIB _libname_) - find_library(CLANG_${_libname_}_LIB ${_libname_} ${LLVM_LIBRARY_DIRS} ${CLANG_LIBRARY_DIRS}) - - if(CLANG_${_libname_}_LIB) - set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_libname_}_LIB}) - endif (CLANG_${_libname_}_LIB) -macro(FIND_AND_ADD_CLANG_LIB) - -# Clang shared library provides just the limited C interface, so it -# can not be used. We look for the static libraries. -FIND_AND_ADD_CLANG_LIB(clang) -FIND_AND_ADD_CLANG_LIB(clangBasic) -FIND_AND_ADD_CLANG_LIB(clangFrontend) -FIND_AND_ADD_CLANG_LIB(clangFrontendTool) -FIND_AND_ADD_CLANG_LIB(clangToolingCore) -FIND_AND_ADD_CLANG_LIB(clangTooling) -FIND_AND_ADD_CLANG_LIB(clangDriver) -FIND_AND_ADD_CLANG_LIB(clangCodeGen) -FIND_AND_ADD_CLANG_LIB(clangEdit) -FIND_AND_ADD_CLANG_LIB(clangSema) -FIND_AND_ADD_CLANG_LIB(clangChecker) -FIND_AND_ADD_CLANG_LIB(clangAnalysis) -FIND_AND_ADD_CLANG_LIB(clangAST) -FIND_AND_ADD_CLANG_LIB(clangParse) -FIND_AND_ADD_CLANG_LIB(clangLex) -FIND_AND_ADD_CLANG_LIB(clangSerialization) -FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) -FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) -FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) -FIND_AND_ADD_CLANG_LIB(clangAnalysis) -FIND_AND_ADD_CLANG_LIB(clangARCMigrate) -FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) -FIND_AND_ADD_CLANG_LIB(clangRewriteCore) -FIND_AND_ADD_CLANG_LIB(clangQuery) -FIND_AND_ADD_CLANG_LIB(clangIndex) -FIND_AND_ADD_CLANG_LIB(clangRename) -FIND_AND_ADD_CLANG_LIB(clangTidy) - -FIND_AND_ADD_CLANG_LIB(LLVMCore) -FIND_AND_ADD_CLANG_LIB(LLVMSupport) - -find_path(CLANG_INCLUDE_DIRS clang/Basic/Version.h HINTS ${LLVM_INCLUDE_DIRS}) - -if(CLANG_LIBS AND CLANG_INCLUDE_DIRS) - message(STATUS "Clang libs: " ${CLANG_LIBS}) - set(CLANG_FOUND TRUE) -endif(CLANG_LIBS AND CLANG_INCLUDE_DIRS) - -if(CLANG_FOUND) - message(STATUS "Found Clang: ${CLANG_INCLUDE_DIRS}") -else(CLANG_FOUND) - if (CLANG_FIND_REQUIRED) - message(FATAL_ERROR "Could NOT find Clang") - endif (CLANG_FIND_REQUIRED) -endif(CLANG_FOUND) diff --git a/cmake/FindClangFormat.cmake b/cmake/FindClangFormat.cmake new file mode 100644 index 0000000000..3e7e79d433 --- /dev/null +++ b/cmake/FindClangFormat.cmake @@ -0,0 +1,98 @@ +# +# Taken from https://raw.githubusercontent.com/BlueBrain/git-cmake-format/master/FindClangFormat.cmake +# --------------- +# +# The module defines the following variables +# +# ``ClangFormat_EXECUTABLE`` Path to clang-format executable +# ``ClangFormat_FOUND`` True if the clang-format executable was found. +# ``ClangFormat_VERSION`` The version of clang-format found +# ``ClangFormat_VERSION_MAJOR`` The clang-format major version if specified, 0 +# otherwise ``ClangFormat_VERSION_MINOR`` The clang-format minor version if +# specified, 0 otherwise ``ClangFormat_VERSION_PATCH`` The clang-format patch +# version if specified, 0 otherwise ``ClangFormat_VERSION_COUNT`` Number of +# version components reported by clang-format +# +# Example usage: +# +# .. code-block:: cmake +# +# find_package(ClangFormat) if(ClangFormat_FOUND) message("clang-format +# executable found: ${ClangFormat_EXECUTABLE}\n" "version: +# ${ClangFormat_VERSION}") endif() + +if(ClangFormat_FOUND) + set(ClangFormat_FIND_QUIETLY TRUE) +endif() + +set(ClangFormat_NAMES + clang-format + clang-format-11 + clang-format-12 +) + +set(ClangFormat_PATHS + /usr/bin + /usr/lib/llvm-11/bin + /usr/lib/llvm-12/bin +) + +find_program(ClangFormat_EXECUTABLE + NAMES ${ClangFormat_NAMES} + DOC "clang-format executable" + PATHS ${ClangFormat_PATHS} +) + +# Extract version from command "clang-format -version" +if(ClangFormat_EXECUTABLE) + execute_process(COMMAND ${ClangFormat_EXECUTABLE} -version + OUTPUT_VARIABLE clang_format_version + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(clang_format_version MATCHES "clang-format version .*") + # clang_format_version sample: "clang-format version 3.9.1-4ubuntu3~16.04.1 + # (tags/RELEASE_391/rc2)" + string(REGEX + REPLACE ".*clang-format version ([.0-9]+).*" + "\\1" + ClangFormat_VERSION + "${clang_format_version}") + # ClangFormat_VERSION sample: "3.9.1" + + # Extract version components + string(REPLACE "." ";" clang_format_version "${ClangFormat_VERSION}") + list(LENGTH clang_format_version ClangFormat_VERSION_COUNT) + if(ClangFormat_VERSION_COUNT GREATER 0) + list(GET clang_format_version 0 ClangFormat_VERSION_MAJOR) + else() + set(ClangFormat_VERSION_MAJOR 0) + endif() + if(ClangFormat_VERSION_COUNT GREATER 1) + list(GET clang_format_version 1 ClangFormat_VERSION_MINOR) + else() + set(ClangFormat_VERSION_MINOR 0) + endif() + if(ClangFormat_VERSION_COUNT GREATER 2) + list(GET clang_format_version 2 ClangFormat_VERSION_PATCH) + else() + set(ClangFormat_VERSION_PATCH 0) + endif() + endif() + unset(clang_format_version) +endif() + +if(ClangFormat_EXECUTABLE AND ClangFormat_VERSION) + set(ClangFormat_FOUND TRUE) + + include(FindPackageHandleStandardArgs) + + # Set standard args + find_package_handle_standard_args(ClangFormat + REQUIRED_VARS ClangFormat_EXECUTABLE + VERSION_VAR ClangFormat_VERSION + ) + + mark_as_advanced(ClangFormat_EXECUTABLE) +else() + set(ClangFormat_FOUND FALSE) +endif() diff --git a/cmake/FindCobol.cmake b/cmake/FindCobol.cmake new file mode 100644 index 0000000000..c85a5cb466 --- /dev/null +++ b/cmake/FindCobol.cmake @@ -0,0 +1,131 @@ +# +# CMake Find GNU/Cobol by Parra Studios +# CMake script to find GNU/Cobol compiler and runtime. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find GNU/Cobol library and include paths +# +# COBOL_FOUND - True if GNU/Cobol was found +# COBOL_EXECUTABLE - GNU/Cobol compiler executable path +# COBOL_VERSION - GNU/Cobol installed version +# COBOL_INCLUDE_DIR - GNU/Cobol headers path +# COBOL_LIBRARY - List of GNU/Cobol libraries + +# Prevent vervosity if already included +if(COBOL_EXECUTABLE) + set(COBOL_FIND_QUITELY TRUE) +endif() + +option(COBOL_CMAKE_DEBUG "Print paths for debugging GNU/Cobol dependencies." OFF) + +include(FindPackageHandleStandardArgs) + +# Find GNU/Cobol compiler executable +find_program(COBOL_EXECUTABLE + NAMES cobc cobc.exe + PATH_SUFFIXES bin + DOC "GNU/Cobol Compiler" +) + +# Find GNU/Cobol version +if(COBOL_EXECUTABLE) + execute_process(COMMAND ${COBOL_EXECUTABLE} --version + OUTPUT_VARIABLE COBOL_VERSION_TAG + RESULT_VARIABLE COBOL_VERSION_TAG_RESULT + ) + + if(COBOL_VERSION_TAG_RESULT EQUAL 0) + # Get first line + string(REGEX REPLACE ";" "\\\\;" COBOL_VERSION_TAG "${COBOL_VERSION_TAG}") + string(REGEX REPLACE "\n" ";" COBOL_VERSION_TAG "${COBOL_VERSION_TAG}") + list(GET COBOL_VERSION_TAG 0 COBOL_VERSION_TAG) + + # Get version + string(REGEX REPLACE " " ";" COBOL_VERSION_TAG "${COBOL_VERSION_TAG}") + list(GET COBOL_VERSION_TAG 2 COBOL_VERSION_TAG) + + # Set version constants + set(COBOL_VERSION "${COBOL_VERSION_TAG}") + string(REPLACE "." ";" COBOL_VERSION_LIST "${COBOL_VERSION}") + list(GET COBOL_VERSION_LIST 0 COBOL_VERSION_MAJOR) + list(GET COBOL_VERSION_LIST 1 COBOL_VERSION_MINOR) + list(GET COBOL_VERSION_LIST 2 COBOL_VERSION_PATCH) + endif() +endif() + +# Find GNU/Cobol include directory +set(COBOL_INCLUDE_PATHS + /usr + /usr/local +) + +set(COBOL_INCLUDE_SUFFIXES + include +) + +find_path(COBOL_INCLUDE_DIR + NAMES libcob.h + PATHS ${COBOL_INCLUDE_PATHS} + PATH_SUFFIXES ${COBOL_INCLUDE_SUFFIXES} + DOC "GNU/Cobol Headers" +) + +# Find library +if(WIN32) + set(COBOL_LIBRARY_SUFFIX "dll") +elseif(APPLE) + set(COBOL_LIBRARY_SUFFIX "dylib") +else() + set(COBOL_LIBRARY_SUFFIX "so") +endif() + +set(COBOL_LIBRARY_NAMES + cob.dll + cob.dylib + libcob.so + libcob.so.${COBOL_VERSION_MAJOR} + libcob.so.${COBOL_VERSION} +) + +set(COBOL_LIBRARY_PATH + /usr/lib + /usr/local/lib +) + +find_library(COBOL_LIBRARY + NAMES ${COBOL_LIBRARY_NAMES} + PATHS ${COBOL_LIBRARY_PATH} + DOC "GNU/Cobol Runtime Library" +) + +# Show debug info if enabled +if(COBOL_CMAKE_DEBUG) + message(STATUS "COBOL_EXECUTABLE: ${COBOL_LIBRARY_NAMES}") + message(STATUS "COBOL_VERSION: ${COBOL_VERSION}") + message(STATUS "COBOL_INCLUDE_DIR: ${COBOL_INCLUDE_DIR}") + message(STATUS "COBOL_LIBRARY: ${COBOL_LIBRARY}") + message(STATUS "COBOL_LIBRARY_SUFFIX: ${COBOL_LIBRARY_SUFFIX}") +endif() + +# Set standard args +find_package_handle_standard_args(COBOL + REQUIRED_VARS COBOL_EXECUTABLE COBOL_INCLUDE_DIR COBOL_LIBRARY COBOL_LIBRARY_SUFFIX + VERSION_VAR COBOL_VERSION +) + +# Mark cmake module as advanced +mark_as_advanced(COBOL_EXECUTABLE COBOL_INCLUDE_DIR COBOL_LIBRARY) diff --git a/cmake/FindCoreCLR.cmake b/cmake/FindCoreCLR.cmake index b6ffe24c0a..884d3c1132 100644 --- a/cmake/FindCoreCLR.cmake +++ b/cmake/FindCoreCLR.cmake @@ -2,7 +2,7 @@ # CMake Find CoreCLR NET Engine by Parra Studios # CMake script to find CoreCLR NET Engine. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,8 +24,6 @@ # CORECLR_LIBRARIES - List of CoreCLR libraries # CORECLR_CGINFO - List of CoreCLR libraries - - # Prevent vervosity if already included if(CORECLR_FOUND) set(CORECLR_FIND_QUIETLY TRUE) diff --git a/cmake/FindDotNET.cmake b/cmake/FindDotNET.cmake index 66a308bbb4..57c2d4dc95 100644 --- a/cmake/FindDotNET.cmake +++ b/cmake/FindDotNET.cmake @@ -2,7 +2,7 @@ # CMake Find Dot NET Engine by Parra Studios # CMake script to find DotNET Engine. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,11 +28,12 @@ if(DOTNET_FOUND) endif() # Define dotnet command -set(DOTNET_COMMAND dotnet) +set(DOTNET_COMMAND dotnet CACHE FILEPATH "Path of .NET Core command") # Detect dotnet command execute_process(COMMAND ${DOTNET_COMMAND} RESULT_VARIABLE DOTNET_COMMAND_RESULT + OUTPUT_QUIET ) # Set found variable (TODO: Review 129 state in Debian) diff --git a/cmake/FindGBench.cmake b/cmake/FindGBench.cmake index 44accc8860..74eacb0b8a 100644 --- a/cmake/FindGBench.cmake +++ b/cmake/FindGBench.cmake @@ -2,7 +2,7 @@ # CMake Find Google Benchmark by Parra Studios # CMake script to find Google Benchmark library. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ find_library(GBENCH_LIBRARY NAMES benchmark) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(google-benchmark +find_package_handle_standard_args(GBench REQUIRED_VARS GBENCH_INCLUDE_DIR GBENCH_LIBRARY ) diff --git a/cmake/FindJulia.cmake b/cmake/FindJulia.cmake new file mode 100644 index 0000000000..bf3c30d3b8 --- /dev/null +++ b/cmake/FindJulia.cmake @@ -0,0 +1,201 @@ +# +# CMake Find Julia Runtime by Parra Studios +# CMake script to find Julia runtime. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Julia runtime library and include paths +# +# JULIA_FOUND - True if Julia runtime was found +# JULIA_EXECUTABLE - Path to the Julia executable +# JULIA_INCLUDE_DIRS - Julia headers path +# JULIA_LIBRARY - List of Julia libraries +# JULIA_LIBRARY_DIR - Folder containing Julia libraries +# JULIA_VERSION - Julia version string +# JULIA_VERSION_MAJOR - Julia major version +# JULIA_VERSION_MINOR - Julia minor version +# JULIA_VERSION_PATCH - Julia patch version +# JULIA_HOME - Folder containing Julia libraries +# JULIA_LLVM_VERSION - Version of LLVM that Julia is using +# JULIA_WORD_SIZE - Whether Julia supports 32 or 64 bits +# +# Options +# +# JULIA_DIR - Defines an alternative search path where Julia runtime is located +# + +# Prevent vervosity if already included +if(JULIA_FOUND) + set(JULIA_FIND_QUIETLY TRUE) +endif() + +# Julia search path +option(JULIA_DIR "Defines an alternative search path where Julia runtime is located." OFF) + +if(JULIA_DIR) + list(APPEND CMAKE_PREFIX_PATH ${JULIA_DIR}) +endif() + +# Julia Executable +find_program(JULIA_EXECUTABLE julia DOC "Julia executable") + +# Julia Version +set(JULIA_VERSION_RESULT 1) + +if(JULIA_EXECUTABLE) + execute_process( + COMMAND "${JULIA_EXECUTABLE}" --startup-file=no --version + OUTPUT_VARIABLE JULIA_VERSION_STRING + RESULT_VARIABLE JULIA_VERSION_RESULT + ) +endif() + +if(JULIA_VERSION_RESULT NOT EQUAL 0) + find_file(JULIA_VERSION_INCLUDE + julia_version.h + PATH_SUFFIXES include/julia + ) + file(READ ${JULIA_VERSION_INCLUDE} JULIA_VERSION_STRING) + string(REGEX MATCH "JULIA_VERSION_STRING.*" JULIA_VERSION_STRING ${JULIA_VERSION_STRING}) +endif() + +string( + REGEX REPLACE ".*([0-9]+\\.[0-9]+\\.[0-9]+).*" "\\1" + JULIA_VERSION_STRING "${JULIA_VERSION_STRING}" +) + +string(REPLACE "." ";" JULIA_VERSION_LIST ${JULIA_VERSION_STRING}) +list(GET JULIA_VERSION_LIST 0 JULIA_VERSION_MAJOR) +list(GET JULIA_VERSION_LIST 1 JULIA_VERSION_MINOR) +list(GET JULIA_VERSION_LIST 2 JULIA_VERSION_PATCH) + +set(JULIA_VERSION ${JULIA_VERSION_STRING} CACHE STRING "Julia version string") + +if(${JULIA_VERSION} VERSION_LESS "0.7.0") + set(JULIA_HOME_NAME "JULIA_HOME") +else() + set(JULIA_HOME_NAME "Sys.BINDIR") + set(JULIA_USING_LIBDL "using Libdl") +endif() + +# Julia Headers +if(JULIA_DIR) + set(JULIA_INCLUDE_DIRS ${JULIA_DIR}/include/julia CACHE STRING "Location of Julia include files") +elseif(JULIA_EXECUTABLE) + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "julia_include_dir = joinpath(match(r\"(.*)(bin)\",${JULIA_HOME_NAME}).captures[1],\"include\",\"julia\")\n + if !isdir(julia_include_dir) # then we're running directly from build\n + julia_base_dir_aux = splitdir(splitdir(${JULIA_HOME_NAME})[1])[1] # useful for running-from-build\n + julia_include_dir = joinpath(julia_base_dir_aux, \"usr\", \"include\" )\n + julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\", \"support\" )\n + julia_include_dir *= \";\" * joinpath(julia_base_dir_aux, \"src\" )\n + end\n + julia_include_dir" + OUTPUT_VARIABLE JULIA_INCLUDE_DIRS + ) + + string(REGEX REPLACE "\"" "" JULIA_INCLUDE_DIRS "${JULIA_INCLUDE_DIRS}") + string(REGEX REPLACE "\n" "" JULIA_INCLUDE_DIRS "${JULIA_INCLUDE_DIRS}") + + set(JULIA_INCLUDE_DIRS ${JULIA_INCLUDE_DIRS} CACHE STRING "Location of Julia include files") +else() + find_path(JULIA_INCLUDE_DIRS + NAMES julia.h + PATHS "/usr/include/julia" + DOC "Location of Julia include files" + ) + get_filename_component(JULIA_INCLUDE_DIRS ${JULIA_INCLUDE_DIRS} DIRECTORY) +endif() + +# Julia Libraries +if(JULIA_EXECUTABLE) + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "${JULIA_USING_LIBDL}\nabspath(Libdl.dlpath((ccall(:jl_is_debugbuild, Cint, ()) != 0) ? \"libjulia-debug\" : \"libjulia\"))" + OUTPUT_VARIABLE JULIA_LIBRARY + ) + + string(REGEX REPLACE "\"" "" JULIA_LIBRARY "${JULIA_LIBRARY}") + string(REGEX REPLACE "\n" "" JULIA_LIBRARY "${JULIA_LIBRARY}") + string(STRIP "${JULIA_LIBRARY}" JULIA_LIBRARY) + + if(WIN32) + get_filename_component(JULIA_LIBRARY_DIR ${JULIA_LIBRARY} DIRECTORY) + get_filename_component(JULIA_LIBRARY_DIR ${JULIA_LIBRARY_DIR} DIRECTORY) + find_library(WIN32_JULIA_LIBRARY + NAMES libjulia.dll.a + PATHS "${JULIA_LIBRARY_DIR}/lib" + NO_DEFAULT_PATH + ) + set(JULIA_LIBRARY "${WIN32_JULIA_LIBRARY}") + endif() + + set(JULIA_LIBRARY "${JULIA_LIBRARY}" CACHE PATH "Julia library") +else() + set(JULIA_LIBRARY_NAMES + libjulia.so.${JULIA_VERSION_MAJOR}.${JULIA_VERSION_MINOR}.${JULIA_VERSION_PATCH} + libjulia.so.${JULIA_VERSION_MAJOR}.${JULIA_VERSION_MINOR} + libjulia.so.${JULIA_VERSION_MAJOR} + libjulia.so + libjulia.${JULIA_VERSION}.dylib + julia + libjulia + libjulia.dll.a + ) + find_library(JULIA_LIBRARY + NAMES ${JULIA_LIBRARY_NAMES} + DOC "Location of Julia library" + CMAKE_FIND_ROOT_PATH_BOTH + ) +endif() + +get_filename_component(JULIA_LIBRARY_DIR ${JULIA_LIBRARY} DIRECTORY) + +# Additional Julia information +if(JULIA_EXECUTABLE) + # Julia Home + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "${JULIA_HOME_NAME}" + OUTPUT_VARIABLE JULIA_HOME + ) + + string(REGEX REPLACE "\"" "" JULIA_HOME "${JULIA_HOME}") + string(REGEX REPLACE "\n" "" JULIA_HOME "${JULIA_HOME}") + + + # LLVM Version + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "Base.libllvm_version" + OUTPUT_VARIABLE JULIA_LLVM_VERSION + ) + + string(REGEX REPLACE "\"" "" JULIA_LLVM_VERSION "${JULIA_LLVM_VERSION}") + string(REGEX REPLACE "\n" "" JULIA_LLVM_VERSION "${JULIA_LLVM_VERSION}") + + # Word Size + execute_process( + COMMAND ${JULIA_EXECUTABLE} --startup-file=no -E "Sys.WORD_SIZE" + OUTPUT_VARIABLE JULIA_WORD_SIZE + ) + string(REGEX REPLACE "\n" "" JULIA_WORD_SIZE "${JULIA_WORD_SIZE}") +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(JULIA + REQUIRED_VARS JULIA_LIBRARY JULIA_LIBRARY_DIR JULIA_INCLUDE_DIRS JULIA_EXECUTABLE JULIA_VERSION + VERSION_VAR JULIA_VERSION + FAIL_MESSAGE "Julia not found" +) diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake deleted file mode 100644 index 43c2f5714f..0000000000 --- a/cmake/FindLLVM.cmake +++ /dev/null @@ -1,104 +0,0 @@ -# Find LLVM -# This module can be used to find LLVM. -# It requires that the llvm-config executable be available on the system path. -# Once found, llvm-config is used for everything else. -# -# Typical usage could be: -# find_package(LLVM QUIET REQUIRED COMPONENTS jit native interpreter) -# -# If the QUIET flag is not set, the specified components and LLVM version are -# outputted. -# -# If the COMPONENTS are not set, the default set of "all" is used. -# -# The following variables are set: -# -# LLVM_FOUND - Set to YES if LLVM is found. -# LLVM_VERSION - Set to the decimal version of the LLVM library. -# LLVM_C_FLAGS - All flags that should be passed to a C compiler. -# LLVM_CXX_FLAGS - All flags that should be passed to a C++ compiler. -# LLVM_CPP_FLAGS - All flags that should be passed to the C pre-processor. -# LLVM_LD_FLAGS - Additional flags to pass to the linker. -# LLVM_LIBRARY_DIRS - A list of directories where the LLVM libraries are located. -# LLVM_INCLUDE_DIRS - A list of directories where the LLVM headers are located. -# LLVM_LIBRARIES - A list of libraries which should be linked against. - -# A macro to run llvm config -macro(_llvm_config _var_name) - # Firstly, locate the LLVM config executable - find_program(_llvm_config_exe - NAMES llvm-config - DOC "llvm-config executable location" - ) - - # If no llvm-config executable was found, set the output variable to not - # found. - if(NOT _llvm_config_exe) - set(${_var_name} "${_var_name}-NOTFOUND") - else(NOT _llvm_config_exe) - # Otherwise, run llvm-config - execute_process( - COMMAND ${_llvm_config_exe} ${ARGN} - OUTPUT_VARIABLE ${_var_name} - RESULT_VARIABLE _llvm_config_retval - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(RESULT_VARIABLE) - message(SEND_ERROR - "Error running llvm-config with arguments: ${ARGN}") - endif(RESULT_VARIABLE) - endif(NOT _llvm_config_exe) -endmacro(_llvm_config) - -include(FindPackageHandleStandardArgs) - -# The default set of components -set(_llvm_components all) - -# If components have been specified via find_package, use them -if(LLVM_FIND_COMPONENTS) - set(_llvm_components ${LLVM_FIND_COMPONENTS}) -endif(LLVM_FIND_COMPONENTS) - -if(NOT LLVM_FIND_QUIETLY) - message(STATUS "Looking for LLVM components: ${_llvm_components}") -endif(NOT LLVM_FIND_QUIETLY) - -_llvm_config(LLVM_VERSION --version) -_llvm_config(LLVM_C_FLAGS --cflags ${_llvm_components}) -_llvm_config(LLVM_CXX_FLAGS --cxxflags ${_llvm_components}) -_llvm_config(LLVM_CPP_FLAGS --cppflags ${_llvm_components}) -_llvm_config(LLVM_LD_FLAGS --ldflags ${_llvm_components}) -_llvm_config(LLVM_LIBRARY_DIRS --libdir ${_llvm_components}) -_llvm_config(LLVM_INCLUDE_DIRS --includedir ${_llvm_components}) -_llvm_config(LLVM_LIBRARIES --libs ${_llvm_components}) - -# Filter preprocessor flags -string(REGEX REPLACE "( )+" ";" LLVM_CPP_FLAGS_LIST ${LLVM_CPP_FLAGS}) - -set(LLVM_CPP_FLAGS_FILTER "") - -foreach(FLAG ${LLVM_CPP_FLAGS_LIST}) - string(REGEX MATCH "^(-I)((/[^/]+)+)" FLAG_MATCH ${FLAG}) - - if("${FLAG_MATCH}" STREQUAL "") - string(REGEX REPLACE "^(-D)" "" FLAG_FILTER ${FLAG}) - - set(FLAG_FILTER "${FLAG_FILTER}=1") - - if(NOT ("${FLAG_FILTER}" STREQUAL "")) - set(LLVM_CPP_FLAGS_FILTER "${LLVM_CPP_FLAGS_FILTER} ${FLAG_FILTER}") - endif() - endif() -endforeach() - -set(LLVM_CPP_FLAGS ${LLVM_CPP_FLAGS_FILTER}) - -if(NOT LLVM_FIND_QUIETLY) - message(STATUS "Found LLVM version: ${LLVM_VERSION}") - message(STATUS "LLVM preprocessor flags: ${LLVM_CPP_FLAGS}") -endif(NOT LLVM_FIND_QUIETLY) - -# Handle the QUIETLY and REQUIRED arguments and set LLVM_FOUND to TRUE if -# all listed variables are TRUE -find_package_handle_standard_args(LLVM DEFAULT_MSG LLVM_LIBRARIES LLVM_INCLUDE_DIRS LLVM_LIBRARY_DIRS) diff --git a/cmake/FindLibClang.cmake b/cmake/FindLibClang.cmake new file mode 100644 index 0000000000..bbf4d2c25f --- /dev/null +++ b/cmake/FindLibClang.cmake @@ -0,0 +1,91 @@ +# +# CMake Find Clang library by Parra Studios +# CMake script to find Clang C API library. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Clang C API library and include paths +# +# LibClang_FOUND - True if Clang library was found +# LibClang_INCLUDE_DIR - Clang headers path +# LibClang_LIBRARY - Clang C API library + +# Prevent vervosity if already included +if(LibClang_FOUND) + set(LibClang_FIND_QUITELY TRUE) +endif() + +option(LibClang_CMAKE_DEBUG "Print paths for debugging LibClang dependencies." OFF) + +# Define what version to search for +if(LibClang_FIND_VERSION) + set(LibClang_VERSION_LIST ${LibClang_FIND_VERSION}) +else() + set(LibClang_VERSION_LIST 17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8) +endif() + +macro(_libclang_generate_search_paths template result) + set(${result}) + foreach(version ${LibClang_VERSION_LIST}) + string(REPLACE "VERSION" "${version}" output_path "${template}") + list(APPEND ${result} ${output_path}) + endforeach() +endmacro() + +include(FindPackageHandleStandardArgs) + +# Find Clang C API Library +_libclang_generate_search_paths("/usr/lib/llvm-VERSION/lib/" LibClang_LIBRARY_PATHS) + +if(NOT LibClang_LIBRARY) + find_library(LibClang_LIBRARY + NAMES clang + PATHS ${LibClang_LIBRARY_PATHS} /usr/lib # Use this path as a fallback + ) +endif() + +# Find Clang C API Headers +_libclang_generate_search_paths("/usr/lib/llvm-VERSION/include/clang-c" LibClang_INCLUDE_PATHS) + +set(LibClang_INCLUDE_HEADERS + BuildSystem.h + CXCompilationDatabase.h + CXErrorCode.h + CXString.h + Documentation.h + Index.h + Platform.h +) + +if(NOT LibClang_INCLUDE_DIR) + find_path(LibClang_INCLUDE_DIR + NAMES ${LibClang_INCLUDE_HEADERS} + PATHS ${LibClang_INCLUDE_PATHS} /usr/include/clang-c # Use this path as a fallback + ) +endif() + +get_filename_component(LibClang_INCLUDE_DIR ${LibClang_INCLUDE_DIR} DIRECTORY) + +# Define LibClang cmake module +find_package_handle_standard_args(LibClang DEFAULT_MSG LibClang_LIBRARY LibClang_INCLUDE_DIR) + +# Mark cmake module as advanced +mark_as_advanced(LibClang_LIBRARY LibClang_INCLUDE_DIR) + +if(LibClang_CMAKE_DEBUG) + message(STATUS "LibClang_INCLUDE_DIRS: ${LibClang_INCLUDE_DIR}") + message(STATUS "LibClang_LIBRARY: ${LibClang_LIBRARY}") +endif() diff --git a/cmake/FindLibFFI.cmake b/cmake/FindLibFFI.cmake index 1ea6aeb0b7..313925b13e 100644 --- a/cmake/FindLibFFI.cmake +++ b/cmake/FindLibFFI.cmake @@ -2,7 +2,7 @@ # CMake Find Foreing Function Interface library by Parra Studios # CMake script to find FFI library. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,9 +20,8 @@ # Find FFI library and include paths # # LIBFFI_FOUND - True if FFI library was found -# LIBFFI_INCLUDE_DIRS - FFI headers path -# LIBFFI_LIBRARIES - List of FFI libraries -# LIBFFI_DEFINITIONS - FFI definitions +# LIBFFI_INCLUDE_DIR - FFI headers path +# LIBFFI_LIBRARY - List of FFI libraries # Prevent vervosity if already included if(LIBFFI_INCLUDE_DIRS) @@ -31,22 +30,54 @@ endif() include(FindPackageHandleStandardArgs) -# Find package configuration module -find_package(PkgConfig) +set(LIBFFI_SUFFIXES + ffi + x86_64-linux-gnu + aarch64-linux-gnu + arm-linux-gnueabi + arm-linux-gnueabihf + i386-linux-gnu + mips64el-linux-gnuabi64 + mipsel-linux-gnu + powerpc64le-linux-gnu + s390x-linux-gnu +) -# Find module -pkg_check_modules(PC_LIBFFI QUIET libffi) +find_library(LIBFFI_LIBRARY + NAMES ffi libffi + PATHS /usr /usr/lib /usr/local /opt/local + PATH_SUFFIXES lib lib64 ${LIBFFI_SUFFIXES} +) -# Find include path -find_path(LIBFFI_INCLUDE_DIR ffi.h HINTS ${PC_LIBFFI_INCLUDEDIR} ${PC_LIBFFI_INCLUDE_DIRS}) +if(APPLE) + execute_process(COMMAND brew --prefix libffi OUTPUT_VARIABLE LIBFFI_PREFIXES) +else() + # TODO: Windows? + set(LIBFFI_PREFIXES) +endif() + +if(NOT LIBFFI_INCLUDE_DIR) + find_path(LIBFFI_INCLUDE_DIR ffi.h + PATHS /usr /usr/include /usr/local /opt/local /usr/include/ffi + PATH_SUFFIXES ${LIBFFI_SUFFIXES} + HINT LIBFFI_PREFIXES + ) +endif() -# Find library -find_library(LIBFFI_LIBRARY NAMES ffi HINTS ${PC_LIBFFI_LIBDIR} ${PC_LIBFFI_LIBRARY_DIRS}) +# Try to load by using PkgConfig +if(NOT LIBFFI_LIBRARY OR NOT LIBFFI_INCLUDE_DIR) + # Find package configuration module + find_package(PkgConfig) -# Define moudle variables -set(LIBFFI_DEFINITIONS ${PC_LIBFFI_CFLAGS_OTHER}) -set(LIBFFI_LIBRARIES ${LIBFFI_LIBRARY}) -set(LIBFFI_INCLUDE_DIRS ${LIBFFI_INCLUDE_DIR}) + # Find module + pkg_check_modules(PC_LIBFFI QUIET libffi) + + # Find include path + find_path(LIBFFI_INCLUDE_DIR ffi.h HINTS ${PC_LIBFFI_INCLUDEDIR} ${PC_LIBFFI_INCLUDE_DIRS}) + + # Find library + find_library(LIBFFI_LIBRARY NAMES ffi HINTS ${PC_LIBFFI_LIBDIR} ${PC_LIBFFI_LIBRARY_DIRS}) +endif() # Define FFI cmake module find_package_handle_standard_args(LibFFI DEFAULT_MSG LIBFFI_LIBRARY LIBFFI_INCLUDE_DIR) diff --git a/cmake/FindLibGit2.cmake b/cmake/FindLibGit2.cmake new file mode 100644 index 0000000000..cb1c0cf0a1 --- /dev/null +++ b/cmake/FindLibGit2.cmake @@ -0,0 +1,82 @@ +# +# CMake Find LibGit2 Library by Parra Studios +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# + +# Find libgit2 library and include paths +# +# LibGit2_FOUND - True if LibGit2 was found +# LibGit2_INCLUDE_DIR - LibGit2 headers path +# LibGit2_VERSION - LibGit2 version +# LibGit2_VERSION_MAJOR - LibGit2 major version +# LibGit2_VERSION_MINOR - LibGit2 minor version +# LibGit2_VERSION_REVISION - LibGit2 patch version +# LibGit2_LIBRARY - LibGit2 shared library +# LibGit2_LIBRARY_DIR - LibGit2 shared library folder +# + +# Prevent vervosity if already included +if(LibGit2_LIBRARY) + set(LibGit2_FIND_QUIETLY TRUE) +endif() + +# Include package manager +include(FindPackageHandleStandardArgs) + +# Find via PkgConfig +find_package(PkgConfig QUIET) +pkg_check_modules(PKG_GIT2 QUIET libgit2) + +if(NOT PKG_GIT2_FOUND) + return() +endif() + +if(NOT LibGit2_DEFINITIONS) + set(LibGit2_DEFINITIONS ${PKG_GIT2_CFLAGS_OTHER}) +endif() + +if(NOT LibGit2_INCLUDE_DIR) + find_path(LibGit2_INCLUDE_DIR + NAMES git2.h + HINTS ${PKG_GIT2_INCLUDE_DIRS} + ) +endif() + +if(NOT LibGit2_VERSION AND LibGit2_INCLUDE_DIR) + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_MAJOR REGEX "^#define LIBGIT2_VER_MAJOR +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_MAJOR "${LibGit2_VERSION_MAJOR}") + + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_MINOR REGEX "^#define LIBGIT2_VER_MINOR +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_MINOR "${LibGit2_VERSION_MINOR}") + + file(STRINGS "${LibGit2_INCLUDE_DIR}/git2/version.h" LibGit2_VERSION_REVISION REGEX "^#define LIBGIT2_VER_REVISION +([0-9]+)") + string(REGEX MATCH "([0-9]+)$" LibGit2_VERSION_REVISION "${LibGit2_VERSION_REVISION}") + + set(LibGit2_VERSION "${LibGit2_VERSION_MAJOR}.${LibGit2_VERSION_MINOR}.${LibGit2_VERSION_REVISION}") +endif() + +if(NOT LibGit2_LIBRARY) + find_library(LibGit2_LIBRARY + NAMES git2 + HINTS ${PKG_GIT2_LIBRARY_DIRS} + ) +endif() + +set(LibGit2_LIBRARIES ${LibGit2_LIBRARY}) +set(LibGit2_INCLUDE_DIRS ${LibGit2_INCLUDE_DIR}) +get_filename_component(LibGit2_LIBRARY_DIR ${LibGit2_LIBRARY} DIRECTORY) + +# Define package +find_package_handle_standard_args(LibGit2 + FOUND_VAR + LibGit2_FOUND + REQUIRED_VARS + LibGit2_LIBRARY + LibGit2_LIBRARY_DIR + LibGit2_INCLUDE_DIR + LibGit2_INCLUDE_DIRS + VERSION_VAR + LibGit2_VERSION +) + +mark_as_advanced(LibGit2_LIBRARY LibGit2_LIBRARY_DIR LibGit2_INCLUDE_DIR) diff --git a/cmake/FindLibTCC.cmake b/cmake/FindLibTCC.cmake new file mode 100644 index 0000000000..83c2e4ac59 --- /dev/null +++ b/cmake/FindLibTCC.cmake @@ -0,0 +1,65 @@ +# +# CMake Find Tiny C Compiler library by Parra Studios +# CMake script to find TCC library. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find TCC library and include paths +# +# LIBTCC_FOUND - True if TCC library was found +# LIBTCC_INCLUDE_DIR - TCC headers path +# LIBTCC_LIBRARY - List of TCC libraries + +# Prevent vervosity if already included +if(LIBTCC_FOUND) + set(LIBTCC_FIND_QUITELY TRUE) +endif() + +include(FindPackageHandleStandardArgs) + +set(LIBTCC_SUFFIXES + x86_64-linux-gnu + aarch64-linux-gnu + arm-linux-gnueabi + arm-linux-gnueabihf + i386-linux-gnu + mips64el-linux-gnuabi64 + mipsel-linux-gnu + powerpc64le-linux-gnu + s390x-linux-gnu +) + +# Require TCC as shared library +set(LIBTCC_LIBRARY_NAMES + ${CMAKE_SHARED_LIBRARY_PREFIX}tcc${CMAKE_SHARED_LIBRARY_SUFFIX} +) + +find_library(LIBTCC_LIBRARY + NAMES ${LIBTCC_LIBRARY_NAMES} + PATHS /usr /usr/lib /usr/local /opt/local + PATH_SUFFIXES lib lib64 ${LIBTCC_SUFFIXES} +) + +find_path(LIBTCC_INCLUDE_DIR libtcc.h + PATHS /usr /usr/include /usr/local /opt/local + PATH_SUFFIXES ${LIBTCC_SUFFIXES} +) + +# Define TCC cmake module +find_package_handle_standard_args(LibTCC DEFAULT_MSG LIBTCC_LIBRARY LIBTCC_INCLUDE_DIR) + +# Mark cmake module as advanced +mark_as_advanced(LIBTCC_INCLUDE_DIR LIBTCC_LIBRARY) diff --git a/cmake/FindMetaCall.cmake b/cmake/FindMetaCall.cmake index 21da128730..f974e9509d 100644 --- a/cmake/FindMetaCall.cmake +++ b/cmake/FindMetaCall.cmake @@ -2,7 +2,7 @@ # CMake Find MetaCall library by Parra Studios # CMake script to find and include MetaCall library for development. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,6 +23,16 @@ # METACALL_INCLUDE_DIRS - MetaCall headers path # METACALL_LIBRARIES - List of MetaCall libraries # METACALL_DEFINITIONS - MetaCall definitions +# +# Example of usage: +# +# # MetaCall Library +# find_package(MetaCall REQUIRED) +# +# if(NOT METACALL_FOUND) +# message(STATUS "MetaCall libraries not found") +# return() +# endif() # TODO: Remove this, the find file must be autogenerated diff --git a/cmake/FindNPM.cmake b/cmake/FindNPM.cmake new file mode 100644 index 0000000000..2a61990e25 --- /dev/null +++ b/cmake/FindNPM.cmake @@ -0,0 +1,89 @@ +# +# CMake Find NPM by Parra Studios +# CMake script to find NodeJS Package Manager. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find NPM executable and paths +# +# NPM_FOUND - True if npm was found +# NPM_GLOBAL_DIR - The global node_modules directory +# NPM_EXECUTABLE - The path to the npm executable +# NPM_VERSION - The version number + +set(NPM_ROOT /usr/bin CACHE STRING "NPM directory") + +find_program(NPM_EXECUTABLE + NAMES npm + HINTS /usr $ENV{NPM_ROOT}/npm ${NPM_ROOT}/npm +) + +if(NOT NPM_EXECUTABLE) + if(NPM_FIND_REQUIRED) + message(FATAL_ERROR "NPM was not found") + endif() + + return() +endif() + +# On Windows set NPM to the cmd version +if(WIN32 AND EXISTS "${NPM_EXECUTABLE}.cmd") + set(NPM_EXECUTABLE "${NPM_EXECUTABLE}.cmd") +endif() + +# Get NPM version +execute_process(COMMAND ${NPM_EXECUTABLE} -v + OUTPUT_VARIABLE NPM_VERSION + ERROR_VARIABLE NPM_VERSION_ERROR + RESULT_VARIABLE NPM_VERSION_CODE +) + +if(NPM_VERSION_CODE) + if(NPM_FIND_REQUIRED) + message(FATAL_ERROR "${NPM_EXECUTABLE} -v failed:\n${NPM_VERSION_CODE}") + endif() +endif() + +if(NPM_VERSION) + string(STRIP ${NPM_VERSION} NPM_VERSION) +endif() + +# Get global node_modules location +execute_process(COMMAND ${NPM_EXECUTABLE} root -g + OUTPUT_VARIABLE NPM_GLOBAL_DIR + ERROR_VARIABLE NPM_GLOBAL_DIR_ERROR + RESULT_VARIABLE NPM_GLOBAL_DIR_CODE +) + +if(NPM_GLOBAL_DIR) + string(STRIP ${NPM_GLOBAL_DIR} NPM_GLOBAL_DIR) +endif() + +if(NPM_GLOBAL_DIR_CODE) + if(NPM_FIND_REQUIRED) + message(FATAL_ERROR "${NPM_EXECUTABLE} root -g failed:\n${NPM_GLOBAL_DIR_ERROR}") + endif() +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(NPM + FOUND_VAR NPM_FOUND + REQUIRED_VARS NPM_EXECUTABLE + VERSION_VAR NPM_VERSION +) + +mark_as_advanced(NPM_FOUND NPM_GLOBAL_DIR NPM_EXECUTABLE NPM_VERSION) diff --git a/cmake/FindNodeJS.cmake b/cmake/FindNodeJS.cmake index 8b20d4acef..6cea42bf48 100644 --- a/cmake/FindNodeJS.cmake +++ b/cmake/FindNodeJS.cmake @@ -1,43 +1,54 @@ # # CMake Find NodeJS JavaScript Runtime by Parra Studios -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Find NodeJS executable and include paths # -# NODEJS_FOUND - True if NodeJS was found -# NODEJS_INCLUDE_DIR - NodeJS headers path -# NODEJS_VERSION - NodeJS version -# NODEJS_VERSION_MAJOR - NodeJS major version -# NODEJS_VERSION_MINOR - NodeJS minor version -# NODEJS_VERSION_PATCH - NodeJS patch version -# NODEJS_MODULE_VERSION - NodeJS module version -# NODEJS_UV_VERSION - UV version of NodeJS -# NODEJS_UV_VERSION_MAJOR - UV major version of NodeJS -# NODEJS_UV_VERSION_MINOR - UV minor version of NodeJS -# NODEJS_UV_VERSION_PATCH - UV patch version of NodeJS -# NODEJS_V8_VERSION - V8 version of NodeJS -# NODEJS_V8_VERSION_MAJOR - V8 major version of NodeJS -# NODEJS_V8_VERSION_MINOR - V8 minor version of NodeJS -# NODEJS_V8_VERSION_PATCH - V8 patch version of NodeJS -# NODEJS_V8_VERSION_TWEAK - V8 patch version of NodeJS -# NODEJS_V8_VERSION_HEX - V8 version of NodeJS in hexadecimal format -# NODEJS_LIBRARY - NodeJS shared library -# NODEJS_EXECUTABLE - NodeJS shell +# NodeJS_FOUND - True if NodeJS was found +# NodeJS_INCLUDE_DIRS - NodeJS headers paths +# NodeJS_VERSION - NodeJS version +# NodeJS_VERSION_MAJOR - NodeJS major version +# NodeJS_VERSION_MINOR - NodeJS minor version +# NodeJS_VERSION_PATCH - NodeJS patch version +# NodeJS_MODULE_VERSION - NodeJS module version +# NodeJS_UV_VERSION - UV version of NodeJS +# NodeJS_UV_VERSION_MAJOR - UV major version of NodeJS +# NodeJS_UV_VERSION_MINOR - UV minor version of NodeJS +# NodeJS_UV_VERSION_PATCH - UV patch version of NodeJS +# NodeJS_V8_VERSION - V8 version of NodeJS +# NodeJS_V8_VERSION_MAJOR - V8 major version of NodeJS +# NodeJS_V8_VERSION_MINOR - V8 minor version of NodeJS +# NodeJS_V8_VERSION_PATCH - V8 patch version of NodeJS +# NodeJS_V8_VERSION_TWEAK - V8 patch version of NodeJS +# NodeJS_V8_VERSION_HEX - V8 version of NodeJS in hexadecimal format +# NodeJS_LIBRARY - NodeJS shared library +# NodeJS_EXECUTABLE - NodeJS shell +# +# Configuration variables: +# +# NodeJS_CMAKE_DEBUG - Print paths for debugging +# NodeJS_EXECUTABLE_ONLY - Find only NodeJS executable (avoid library and include files) +# NodeJS_SHARED_UV - If it is enabled, libuv won't be required by this script +# NodeJS_BUILD_FROM_SOURCE - If it is enabled, NodeJS runtime library will be built from source +# NodeJS_BUILD_WITHOUT_ICU - If it is enabled, NodeJS runtime library will be built without internationalization support +# NodeJS_INSTALL_PREFIX - Define a custom install prefix for NodeJS (Linux / Darwin only) # Prevent vervosity if already included -if(NODEJS_INCLUDE_DIR) - set(NODEJS_FIND_QUIETLY TRUE) +if(NodeJS_EXECUTABLE) + set(NodeJS_FIND_QUIETLY TRUE) endif() -# Debug flag -set(_NODEJS_CMAKE_DEBUG TRUE) +option(NodeJS_CMAKE_DEBUG "Print paths for debugging NodeJS dependencies." OFF) +option(NodeJS_SHARED_UV "If it is enabled, libuv won't be required by this script." OFF) +option(NodeJS_BUILD_FROM_SOURCE "If it is enabled, NodeJS runtime library will be built from source." OFF) +option(NodeJS_BUILD_WITHOUT_ICU "If it is enabled, NodeJS runtime library will be built without internationalization support." OFF) # Include package manager include(FindPackageHandleStandardArgs) # NodeJS paths -set(NODEJS_PATHS +set(NodeJS_PATHS ${NODE_HOME} ${NODE_ROOT} $ENV{ProgramFiles}/node @@ -59,24 +70,21 @@ set(NODEJS_PATHS # Find NodeJS include directories if(MSVC OR CMAKE_BUILD_TYPE EQUAL "Debug") - set(NODEJS_V8_HEADERS v8.h v8-debug.h v8-profiler.h v8stdint.h v8-version.h) + set(NodeJS_V8_HEADERS v8.h v8-version.h v8-profiler.h) # v8-debug.h else() - set(NODEJS_V8_HEADERS v8.h v8stdint.h v8-version.h) + set(NodeJS_V8_HEADERS v8.h v8-version.h) endif() -set(NODEJS_UV_HEADERS uv.h) # TODO: Add uv-(platform).h? - -find_path(NODEJS_UV_INCLUDE_DIR ${NODEJS_UV_HEADERS} - PATHS /usr/include/compat-libuv010 - NO_DEFAULT_PATH -) +if(NOT NodeJS_SHARED_UV) + set(NodeJS_UV_HEADERS uv.h) +endif() -set(NODEJS_HEADERS +set(NodeJS_HEADERS node.h - ${NODEJS_V8_HEADERS} + node_api.h ) -set(NODEJS_INCLUDE_SUFFIXES +set(NodeJS_INCLUDE_SUFFIXES include include/node include/nodejs @@ -85,271 +93,573 @@ set(NODEJS_INCLUDE_SUFFIXES include/nodejs/src include/nodejs/deps/v8/include include/nodejs/deps/uv/include + src ) -if(NOT NODEJS_UV_INCLUDE_DIR) - set(NODEJS_HEADERS - ${NODEJS_HEADERS} - ${NODEJS_UV_HEADERS} - ) - - set(NODEJS_INCLUDE_SUFFIXES - ${NODEJS_INCLUDE_SUFFIXES} - include/deps/uv/include - ) -endif() - -set(NODEJS_INCLUDE_PATHS +set(NodeJS_INCLUDE_PATHS /usr -) - -find_path(NODEJS_INCLUDE_DIR ${NODEJS_HEADERS} - PATHS ${NODEJS_INCLUDE_PATHS} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} - DOC "NodeJS JavaScript Runtime Headers" + /usr/local ) # Find NodeJS executable -find_program(NODEJS_EXECUTABLE - NAMES node nodejs node.exe - HINTS ${NODEJS_PATHS} - PATH_SUFFIXES bin - DOC "NodeJS JavaScript Runtime Interpreter" -) +if(NOT NodeJS_EXECUTABLE) + find_program(NodeJS_EXECUTABLE + NAMES node nodejs node.exe + HINTS ${NodeJS_PATHS} + PATH_SUFFIXES bin + DOC "NodeJS JavaScript Runtime Interpreter" + ) +endif() -if(NODEJS_EXECUTABLE) +if(NodeJS_EXECUTABLE AND NOT NodeJS_VERSION) # Detect NodeJS version - execute_process(COMMAND ${NODEJS_EXECUTABLE} --version - OUTPUT_VARIABLE NODEJS_VERSION_TAG - RESULT_VARIABLE NODEJS_VERSION_TAG_RESULT + execute_process(COMMAND ${NodeJS_EXECUTABLE} --version + OUTPUT_VARIABLE NodeJS_VERSION_TAG + RESULT_VARIABLE NodeJS_VERSION_TAG_RESULT ) - if(NODEJS_VERSION_TAG_RESULT EQUAL 0) - string(REPLACE "v" "" NODEJS_VERSION_TAG "${NODEJS_VERSION_TAG}") - string(REPLACE "\n" "" NODEJS_VERSION_TAG "${NODEJS_VERSION_TAG}") - set(NODEJS_VERSION "${NODEJS_VERSION_TAG}") - string(REPLACE "." ";" NODEJS_VERSION_LIST "${NODEJS_VERSION}") - list(GET NODEJS_VERSION_LIST 0 NODEJS_VERSION_MAJOR) - list(GET NODEJS_VERSION_LIST 1 NODEJS_VERSION_MINOR) - list(GET NODEJS_VERSION_LIST 2 NODEJS_VERSION_PATCH) + if(NodeJS_VERSION_TAG_RESULT EQUAL 0) + string(REPLACE "v" "" NodeJS_VERSION_TAG "${NodeJS_VERSION_TAG}") + string(REPLACE "\n" "" NodeJS_VERSION_TAG "${NodeJS_VERSION_TAG}") + set(NodeJS_VERSION "${NodeJS_VERSION_TAG}") + string(REPLACE "." ";" NodeJS_VERSION_LIST "${NodeJS_VERSION}") + list(GET NodeJS_VERSION_LIST 0 NodeJS_VERSION_MAJOR) + list(GET NodeJS_VERSION_LIST 1 NodeJS_VERSION_MINOR) + list(GET NodeJS_VERSION_LIST 2 NodeJS_VERSION_PATCH) endif() # Detect UV version - execute_process(COMMAND ${NODEJS_EXECUTABLE} -e "console.log(process.versions.uv)" - OUTPUT_VARIABLE NODEJS_UV_VERSION_TAG - RESULT_VARIABLE NODEJS_UV_VERSION_TAG_RESULT + execute_process(COMMAND ${NodeJS_EXECUTABLE} -e "console.log(process.versions.uv)" + OUTPUT_VARIABLE NodeJS_UV_VERSION_TAG + RESULT_VARIABLE NodeJS_UV_VERSION_TAG_RESULT ) - if(NODEJS_VERSION_TAG_RESULT EQUAL 0) - string(REPLACE "\n" "" NODEJS_UV_VERSION_TAG "${NODEJS_UV_VERSION_TAG}") - set(NODEJS_UV_VERSION "${NODEJS_UV_VERSION_TAG}") - string(REPLACE "." ";" NODEJS_UV_VERSION_LIST "${NODEJS_UV_VERSION}") - list(GET NODEJS_UV_VERSION_LIST 0 NODEJS_UV_VERSION_MAJOR) - list(GET NODEJS_UV_VERSION_LIST 1 NODEJS_UV_VERSION_MINOR) - list(GET NODEJS_UV_VERSION_LIST 2 NODEJS_UV_VERSION_PATCH) + if(NodeJS_VERSION_TAG_RESULT EQUAL 0) + string(REPLACE "\n" "" NodeJS_UV_VERSION_TAG "${NodeJS_UV_VERSION_TAG}") + set(NodeJS_UV_VERSION "${NodeJS_UV_VERSION_TAG}") + string(REPLACE "." ";" NodeJS_UV_VERSION_LIST "${NodeJS_UV_VERSION}") + list(GET NodeJS_UV_VERSION_LIST 0 NodeJS_UV_VERSION_MAJOR) + list(GET NodeJS_UV_VERSION_LIST 1 NodeJS_UV_VERSION_MINOR) + list(GET NodeJS_UV_VERSION_LIST 2 NodeJS_UV_VERSION_PATCH) endif() # TODO: Implement V8 version by command? -endif() + # Check if NodeJS executable only is requested + if(NodeJS_EXECUTABLE_ONLY) + find_package_handle_standard_args(NodeJS + REQUIRED_VARS NodeJS_EXECUTABLE + VERSION_VAR NodeJS_VERSION + ) -# Detect NodeJS V8 version -if(NODEJS_INCLUDE_DIR) - find_file(NODEJS_V8_VERSION_FILE_PATH v8-version.h - PATHS ${NODEJS_INCLUDE_DIR} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} - DOC "NodeJS V8 JavaScript Version Header" - ) + mark_as_advanced(NodeJS_EXECUTABLE) + + if(NodeJS_CMAKE_DEBUG) + message(STATUS "NodeJS_VERSION: ${NodeJS_VERSION}") + message(STATUS "NodeJS_EXECUTABLE: ${NodeJS_EXECUTABLE}") + endif() + + return() + endif() +endif() - if(NODEJS_V8_VERSION_FILE_PATH) - file(READ ${NODEJS_V8_VERSION_FILE_PATH} NODEJS_V8_VERSION_FILE) +if(NOT NodeJS_BUILD_FROM_SOURCE) + if(NOT NodeJS_INCLUDE_DIR) + # Find NodeJS includes + find_path(NodeJS_INCLUDE_DIR + NAMES ${NodeJS_HEADERS} + PATHS ${NodeJS_INCLUDE_PATHS} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime Headers" + ) + endif() - string(REGEX MATCH "#define V8_MAJOR_VERSION ([0-9]+)" NODEJS_V8_VERSION_MAJOR_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_MAJOR ${NODEJS_V8_VERSION_MAJOR_DEF}) + # Check if the include directory contains all headers in the same folder + if(NodeJS_INCLUDE_DIR) + foreach(HEADER IN ITEMS ${NodeJS_HEADERS}) + if(NOT EXISTS ${NodeJS_INCLUDE_DIR}/${HEADER}) + message(WARNING "NodeJS header ${HEADER} not found in ${NodeJS_INCLUDE_DIR}") + unset(NodeJS_INCLUDE_DIR CACHE) + break() + endif() + endforeach() + endif() - string(REGEX MATCH "#define V8_MINOR_VERSION ([0-9]+)" NODEJS_V8_VERSION_MINOR_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_MINOR ${NODEJS_V8_VERSION_MINOR_DEF}) + # Find NodeJS V8 includes + if(NOT NodeJS_V8_INCLUDE_DIR) + find_path(NodeJS_V8_INCLUDE_DIR + NAMES ${NodeJS_V8_HEADERS} + PATHS ${NodeJS_INCLUDE_PATHS} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime V8 Headers" + ) + endif() - string(REGEX MATCH "#define V8_BUILD_NUMBER ([0-9]+)" NODEJS_V8_VERSION_PATCH_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_PATCH ${NODEJS_V8_VERSION_PATCH_DEF}) + # Check if the include directory contains all headers in the same folder + if(NodeJS_V8_INCLUDE_DIR) + foreach(HEADER IN ITEMS ${NodeJS_V8_HEADERS}) + if(NOT EXISTS ${NodeJS_V8_INCLUDE_DIR}/${HEADER}) + message(WARNING "NodeJS header ${HEADER} not found in ${NodeJS_V8_INCLUDE_DIR}") + unset(NodeJS_V8_INCLUDE_DIR CACHE) + break() + endif() + endforeach() + endif() - string(REGEX MATCH "#define V8_PATCH_LEVEL ([0-9]+)" NODEJS_V8_VERSION_TWEAK_DEF ${NODEJS_V8_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_V8_VERSION_TWEAK ${NODEJS_V8_VERSION_TWEAK_DEF}) + # Find NodeJS UV includes + if(NOT NodeJS_UV_INCLUDE_DIR) + find_path(NodeJS_UV_INCLUDE_DIR + NAMES ${NodeJS_UV_HEADERS} + PATHS ${NodeJS_INCLUDE_PATHS} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime UV Headers" + ) + endif() - set(NODEJS_V8_VERSION "${NODEJS_V8_VERSION_MAJOR}.${NODEJS_V8_VERSION_MINOR}.${NODEJS_V8_VERSION_PATCH}.${NODEJS_V8_VERSION_TWEAK}") + # Check if the include directory contains all headers in the same folder + if(NodeJS_UV_INCLUDE_DIR) + foreach(HEADER IN ITEMS ${NodeJS_UV_HEADERS}) + if(NOT EXISTS ${NodeJS_UV_INCLUDE_DIR}/${HEADER}) + message(WARNING "NodeJS header ${HEADER} not found in ${NodeJS_UV_INCLUDE_DIR}") + unset(NodeJS_UV_INCLUDE_DIR CACHE) + break() + endif() + endforeach() + endif() +endif() - set(NODEJS_V8_VERSION_HEX 0x0${NODEJS_V8_VERSION_MAJOR}${NODEJS_V8_VERSION_MINOR}${NODEJS_V8_VERSION_PATCH}${NODEJS_V8_VERSION_TWEAK}) - string(LENGTH "${NODEJS_V8_VERSION_HEX}" NODEJS_V8_VERSION_HEX_LENGTH) +# Download includes in case they are not distributed +if(NOT NodeJS_INCLUDE_DIR OR NOT NodeJS_V8_INCLUDE_DIR OR NOT NodeJS_UV_INCLUDE_DIR) + if(NOT NodeJS_VERSION) + # We do not have any way to know what version to install + message(WARNING "NodeJS headers could not be found, neither a valid NodeJS version.") + return() + endif() - while(NODEJS_V8_VERSION_HEX_LENGTH LESS 8) + # NodeJS download and output path (workaround for NodeJS headers) + set(NodeJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNodeJS_VERSION%7D/node-v$%7BNodeJS_VERSION%7D-headers.tar.gz") + set(NodeJS_BASE_PATH "${CMAKE_CURRENT_BINARY_DIR}/headers") + set(NodeJS_DOWNLOAD_FILE "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}-headers.tar.gz") + set(NodeJS_HEADERS_OUTPUT_PATH "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}") - set(NODEJS_V8_VERSION_HEX "${NODEJS_V8_VERSION_HEX}0") - string(LENGTH "${NODEJS_V8_VERSION_HEX}" NODEJS_V8_VERSION_HEX_LENGTH) + # Download node if needed + if(NOT EXISTS "${NodeJS_DOWNLOAD_FILE}") + message(STATUS "Downloading NodeJS headers") + file(DOWNLOAD ${NodeJS_DOWNLOAD_URL} ${NodeJS_DOWNLOAD_FILE} SHOW_PROGRESS) + endif() - endwhile() + # Decompress node if needed + if(NOT EXISTS "${NodeJS_HEADERS_OUTPUT_PATH}") + message(STATUS "Extract NodeJS headers") + execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NodeJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${NodeJS_BASE_PATH}" OUTPUT_QUIET) endif() - # Get node version - find_file(NODEJS_VERSION_FILE_PATH node_version.h - PATHS ${NODEJS_INCLUDE_DIR} - PATH_SUFFIXES ${NODEJS_INCLUDE_SUFFIXES} + # Find NodeJS includes + find_path(NodeJS_INCLUDE_DIR + NAMES ${NodeJS_HEADERS} + PATHS ${NodeJS_HEADERS_OUTPUT_PATH} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime Headers" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) + + # Find NodeJS V8 includes + find_path(NodeJS_V8_INCLUDE_DIR + NAMES ${NodeJS_V8_HEADERS} + PATHS ${NodeJS_HEADERS_OUTPUT_PATH} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime V8 Headers" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) + + # Find NodeJS UV includes + find_path(NodeJS_UV_INCLUDE_DIR + NAMES ${NodeJS_UV_HEADERS} + PATHS ${NodeJS_HEADERS_OUTPUT_PATH} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Runtime UV Headers" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) +endif() + +if(NodeJS_INCLUDE_DIR) + # Get node module version + find_file(NodeJS_VERSION_FILE_PATH node_version.h + PATHS ${NodeJS_INCLUDE_DIR} DOC "NodeJS JavaScript Version Header" ) - if(NODEJS_VERSION_FILE_PATH) - file(READ ${NODEJS_VERSION_FILE_PATH} NODEJS_VERSION_FILE) + if(NOT NodeJS_VERSION_FILE_PATH) + find_file(NodeJS_VERSION_FILE_PATH node_version.h + PATHS ${NodeJS_INCLUDE_DIR} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS JavaScript Version Header" + ) + endif() + + if(NodeJS_VERSION_FILE_PATH) + file(READ ${NodeJS_VERSION_FILE_PATH} NodeJS_VERSION_FILE) - string(REGEX MATCH "#define NODE_MODULE_VERSION ([0-9]+)" NODEJS_MODULE_VERSION_DEF ${NODEJS_VERSION_FILE}) - string(REGEX MATCH "([0-9]+)$" NODEJS_MODULE_VERSION ${NODEJS_MODULE_VERSION_DEF}) + string(REGEX MATCH "#define NODE_MODULE_VERSION ([0-9]+)" NodeJS_MODULE_VERSION_DEF ${NodeJS_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_MODULE_VERSION ${NodeJS_MODULE_VERSION_DEF}) endif() endif() -# TODO: Remove this workaround when NodeJS begins to distribute node as a shared library (maybe never?) +if(NodeJS_V8_INCLUDE_DIR) + # Detect NodeJS V8 version + find_file(NodeJS_V8_VERSION_FILE_PATH v8-version.h + PATHS ${NodeJS_V8_INCLUDE_DIR} + PATH_SUFFIXES ${NodeJS_INCLUDE_SUFFIXES} + DOC "NodeJS V8 JavaScript Version Header" + ) -# NodeJS library names -set(NODEJS_LIBRARY_NAMES - libnode.so.${NODEJS_MODULE_VERSION} - libnode.${NODEJS_MODULE_VERSION}.dylib - libnode.${NODEJS_MODULE_VERSION}.dll - node.lib -) + if(NodeJS_V8_VERSION_FILE_PATH) + file(READ ${NodeJS_V8_VERSION_FILE_PATH} NodeJS_V8_VERSION_FILE) -if(WIN32) - set(NODEJS_COMPILE_PATH "${NODEJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}") -else() - set(NODEJS_COMPILE_PATH "${NODEJS_OUTPUT_PATH}/out/${CMAKE_BUILD_TYPE}") + string(REGEX MATCH "#define V8_MAJOR_VERSION ([0-9]+)" NodeJS_V8_VERSION_MAJOR_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_MAJOR ${NodeJS_V8_VERSION_MAJOR_DEF}) + + string(REGEX MATCH "#define V8_MINOR_VERSION ([0-9]+)" NodeJS_V8_VERSION_MINOR_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_MINOR ${NodeJS_V8_VERSION_MINOR_DEF}) + + string(REGEX MATCH "#define V8_BUILD_NUMBER ([0-9]+)" NodeJS_V8_VERSION_PATCH_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_PATCH ${NodeJS_V8_VERSION_PATCH_DEF}) + + string(REGEX MATCH "#define V8_PATCH_LEVEL ([0-9]+)" NodeJS_V8_VERSION_TWEAK_DEF ${NodeJS_V8_VERSION_FILE}) + string(REGEX MATCH "([0-9]+)$" NodeJS_V8_VERSION_TWEAK ${NodeJS_V8_VERSION_TWEAK_DEF}) + + set(NodeJS_V8_VERSION "${NodeJS_V8_VERSION_MAJOR}.${NodeJS_V8_VERSION_MINOR}.${NodeJS_V8_VERSION_PATCH}.${NodeJS_V8_VERSION_TWEAK}") + + set(NodeJS_V8_VERSION_HEX 0x0${NodeJS_V8_VERSION_MAJOR}${NodeJS_V8_VERSION_MINOR}${NodeJS_V8_VERSION_PATCH}${NodeJS_V8_VERSION_TWEAK}) + string(LENGTH "${NodeJS_V8_VERSION_HEX}" NodeJS_V8_VERSION_HEX_LENGTH) + + while(NodeJS_V8_VERSION_HEX_LENGTH LESS 8) + + set(NodeJS_V8_VERSION_HEX "${NodeJS_V8_VERSION_HEX}0") + string(LENGTH "${NodeJS_V8_VERSION_HEX}" NodeJS_V8_VERSION_HEX_LENGTH) + + endwhile() + endif() endif() -if(WIN32) - set(NODEJS_LIBRARY_PATH "${NODEJS_COMPILE_PATH}") # TODO: Set a valid install path -else() - set(NODEJS_LIBRARY_PATH "/usr/local/lib") +# Find NodeJS library from module version +if(NodeJS_MODULE_VERSION) + # NodeJS library names + if(WIN32) + if(NodeJS_VERSION_MAJOR GREATER_EQUAL 14) + set(NodeJS_LIBRARY_NAMES + libnode.${NodeJS_MODULE_VERSION}.dll + libnode.dll + libnode.lib + ) + else() + set(NodeJS_LIBRARY_NAMES + node.${NodeJS_MODULE_VERSION}.dll + node.dll + node.lib + ) + endif() + else() + set(NodeJS_LIBRARY_NAMES + libnode.so.${NodeJS_MODULE_VERSION} + libnode.so + libnode.${NodeJS_MODULE_VERSION}.dylib + libnode.dylib + ) + endif() + + if(NOT NodeJS_BUILD_FROM_SOURCE) + message(STATUS "Searching NodeJS library version ${NodeJS_MODULE_VERSION}") + + # Find library + find_library(NodeJS_LIBRARY + NAMES ${NodeJS_LIBRARY_NAMES} + HINTS ${NodeJS_PATHS} + PATH_SUFFIXES lib + DOC "NodeJS JavaScript Runtime Library" + ) + + if(NOT NodeJS_LIBRARY) + if(WIN32) + set(NodeJS_LIBRARY_PATH "C:/Program Files/nodejs") + else() + set(NodeJS_LIBRARY_PATH "/usr/local/lib" "/usr/lib") + endif() + + set(NodeJS_SYSTEM_LIBRARY_PATH "/lib/x86_64-linux-gnu" "/usr/lib/x86_64-linux-gnu") # TODO: Add others + + find_library(NodeJS_LIBRARY + NAMES ${NodeJS_LIBRARY_NAMES} + PATHS ${NodeJS_LIBRARY_PATH} ${NodeJS_SYSTEM_LIBRARY_PATH} + DOC "NodeJS JavaScript Runtime Library" + ) + endif() + + if(NodeJS_LIBRARY) + message(STATUS "NodeJS Library Found") + endif() + endif() endif() -# Find library -find_library(NODEJS_LIBRARY - NAMES ${NODEJS_LIBRARY_NAMES} - PATHS ${NODEJS_COMPILE_PATH} ${NODEJS_LIBRARY_PATH} - DOC "NodeJS JavaScript Runtime Library" -) +# Install NodeJS library in case it is not distributed +if(NOT NodeJS_LIBRARY) + message(STATUS "NodeJS library not found, trying to build it from source") -if(NOT NODEJS_LIBRARY) # NodeJS download and output path (workaround to compile node as a shared library) - set(NODEJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNODEJS_VERSION%7D/node-v$%7BNODEJS_VERSION%7D.tar.gz") - set(NODEJS_DOWNLOAD_FILE "${CMAKE_CURRENT_BINARY_DIR}/node-v${NODEJS_VERSION}.tar.gz") - set(NODEJS_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/node-v${NODEJS_VERSION}") + set(NodeJS_DOWNLOAD_URL "https://nodejs.org/dist/v$%7BNodeJS_VERSION%7D/node-v$%7BNodeJS_VERSION%7D.tar.gz") + set(NodeJS_BASE_PATH "${CMAKE_CURRENT_BINARY_DIR}/sources") + set(NodeJS_DOWNLOAD_FILE "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}.tar.gz") + set(NodeJS_OUTPUT_PATH "${NodeJS_BASE_PATH}/node-v${NodeJS_VERSION}") # Download node if needed - if(NOT EXISTS "${NODEJS_DOWNLOAD_FILE}") - message(STATUS "Downloading NodeJS distribution") - file(DOWNLOAD ${NODEJS_DOWNLOAD_URL} ${NODEJS_DOWNLOAD_FILE}) + if(NOT EXISTS "${NodeJS_DOWNLOAD_FILE}") + message(STATUS "Downloading NodeJS distribution v${NodeJS_VERSION}") + file(DOWNLOAD ${NodeJS_DOWNLOAD_URL} ${NodeJS_DOWNLOAD_FILE} SHOW_PROGRESS) endif() # Decompress node if needed - if(NOT EXISTS "${NODEJS_OUTPUT_PATH}") + if(NOT EXISTS "${NodeJS_OUTPUT_PATH}") message(STATUS "Extract NodeJS distribution") - execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NODEJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" OUTPUT_QUIET) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvf" "${NodeJS_DOWNLOAD_FILE}" WORKING_DIRECTORY "${NodeJS_BASE_PATH}" OUTPUT_QUIET) + endif() + + if(WIN32) + if(NodeJS_VERSION_MAJOR LESS 14) + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}") + else() + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/out/${CMAKE_BUILD_TYPE}") + endif() + else() + if(NodeJS_VERSION_MAJOR LESS 14) + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/out") + else() + set(NodeJS_COMPILE_PATH "${NodeJS_OUTPUT_PATH}/out/${CMAKE_BUILD_TYPE}") + endif() endif() # Compile node as a shared library if needed - if(NOT EXISTS "${NODEJS_COMPILE_PATH}") - if(WIN32) - if(NOT EXISTS "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/node.dll") + if(NOT EXISTS "${NodeJS_COMPILE_PATH}") + if(WIN32 AND MSVC) + if(NOT EXISTS "${NodeJS_COMPILE_PATH}/node.dll" AND NOT EXISTS "${NodeJS_COMPILE_PATH}/libnode.dll") message(STATUS "Build NodeJS shared library") if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86") - set(NODEJS_COMPILE_ARCH "x86") + set(NodeJS_COMPILE_ARCH "x86") else() - set(NODEJS_COMPILE_ARCH "x64") + set(NodeJS_COMPILE_ARCH "x64") endif() - # Check vs2017 or vs2015 (TODO: Add more versions if they are supported by NodeJS) - if(MSVC_VERSION EQUAL 1900) - set(NODEJS_MSVC_VER vs2015) + # Check for Visual Studio Version and configure the build command + if(MSVC_VERSION GREATER_EQUAL 1930) + set(NodeJS_MSVC_VER vs2022) + elseif(MSVC_VERSION GREATER_EQUAL 1920) + set(NodeJS_MSVC_VER vs2019) elseif(MSVC_VERSION GREATER 1900) - set(NODEJS_MSVC_VER vs2017) + set(NodeJS_MSVC_VER vs2017) + elseif(MSVC_VERSION EQUAL 1900) + set(NodeJS_MSVC_VER vs2015) + else() + message(FATAL_ERROR "Version of Visual Studio too old, add other toolsets in FindNodeJS.cmake in order to support them") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(NODEJS_BUILD_TYPE debug) + set(NodeJS_BUILD_TYPE debug) else() - set(NODEJS_BUILD_TYPE release) + set(NodeJS_BUILD_TYPE release) endif() - execute_process(COMMAND vcbuild.bat dll ${NODEJS_BUILD_TYPE} ${NODEJS_COMPILE_ARCH} ${NODEJS_MSVC_VER} WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + if(NodeJS_BUILD_WITHOUT_ICU) + set(BUILD_ICU_FLAGS without-intl) + else() + set(BUILD_ICU_FLAGS) + endif() - # TODO: Implement msi build - # execute_process(COMMAND vcbuild.bat dll ${NODEJS_BUILD_TYPE} ${NODEJS_COMPILE_ARCH} ${NODEJS_MSVC_VER} msi WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + # Building NodeJS 14 as library in Windows is broken (so we need to patch it) + if(WIN32 AND NodeJS_VERSION_MAJOR GREATER_EQUAL 14) + find_package(Python COMPONENTS Interpreter REQUIRED) - # Copy library to MetaCall output path - file(COPY ${NODEJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}/node.dll DESTINATION ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/node.dll) + execute_process( + COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/cmake/NodeJSGYPPatch.py ${NodeJS_OUTPUT_PATH}/node.gyp + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + RESULT_VARIABLE NodeJS_PATCH_SCRIPT + ) - message(STATUS "Install NodeJS shared library") + if(NOT NodeJS_PATCH_SCRIPT EQUAL 0) + message(FATAL_ERROR "FindNodeJS.cmake failed to patch node.gyp project") + endif() + endif() - # TODO: Implement install command - #execute_process(COMMAND msiexec /a "node-v${NODEJS_VERSION}-${NODEJS_COMPILE_ARCH}.msi" WORKING_DIRECTORY "${NODEJS_COMPILE_PATH}" OUTPUT_QUIET) - endif() + execute_process( + COMMAND vcbuild.bat dll ${NodeJS_BUILD_TYPE} ${NodeJS_COMPILE_ARCH} ${NodeJS_MSVC_VER} ${BUILD_ICU_FLAGS} + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + ) + + if(EXISTS "${NodeJS_COMPILE_PATH}/node.dll") + set(NodeJS_LIBRARY_NAME "node.dll") + elseif(EXISTS "${NodeJS_COMPILE_PATH}/libnode.dll") + set(NodeJS_LIBRARY_NAME "libnode.dll") + else() + message(FATAL_ERROR "FindNodeJS.cmake failed to build node library") + endif() - # TODO: Delete this workaround after implementing the install command - set(NODEJS_LIBRARY_PATH ${NODEJS_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) + # Copy library to MetaCall output path + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_OUTPUT_DIR}) + file(COPY "${NodeJS_COMPILE_PATH}/${NodeJS_LIBRARY_NAME}" DESTINATION ${PROJECT_OUTPUT_DIR}) + endif() else() message(STATUS "Configure NodeJS shared library") - # TODO: Select correct ICU version depending on NodeJS version - # http://download.icu-project.org/files/icu4c/60.2/icu4c-60_2-src.tgz - # http://download.icu-project.org/files/icu4c/61.1/icu4c-61_1-src.tgz - # http://download.icu-project.org/files/icu4c/62.1/icu4c-62_1-src.tgz - # http://download.icu-project.org/files/icu4c/64.2/icu4c-64_2-src.tgz + if(NodeJS_BUILD_WITHOUT_ICU) + set(BUILD_ICU_FLAGS "--without-intl") + else() + # Select the ICU library depending on the NodeJS version + if("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "20") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-73-1/icu4c-73_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "18") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-72-1/icu4c-72_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "16") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-69-1/icu4c-69_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "15") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-67-1/icu4c-67_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "14") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-66-1/icu4c-66_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "12") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-65-1/icu4c-65_1-src.zip") + elseif("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "10") + set(ICU_URL "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-src.zip") + endif() + + # Workaround for configure bug: ERROR: Could not load deps/icu/source/common/unicode/uvernum.h - is ICU installed? + if("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "18") + set(NodeJS_ICU_DOWNLOAD_FILE "${NodeJS_BASE_PATH}/icu4c-src.zip") + file(DOWNLOAD ${ICU_URL} "${NodeJS_ICU_DOWNLOAD_FILE}") + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}/deps" + COMMAND ${CMAKE_COMMAND} -E tar xzf "${NodeJS_ICU_DOWNLOAD_FILE}" + ) + set(ICU_URL "${NodeJS_OUTPUT_PATH}/deps/icu") + endif() + + # Workaround for OpenSSL bug: https://github.com/metacall/core/issues/223 + if(APPLE) + set(ICU_ENV_VAR ${CMAKE_COMMAND} -E env PYTHONHTTPSVERIFY=0) + else() + set(ICU_ENV_VAR) + endif() + + set(BUILD_ICU_FLAGS "--with-icu-source=${ICU_URL}") + endif() - if("${CMAKE_BUILD_TYPE}" EQUAL "Debug") - execute_process(COMMAND sh -c "./configure --with-icu-source=http://download.icu-project.org/files/icu4c/64.2/icu4c-64_2-src.tgz --shared --debug" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + if(NodeJS_INSTALL_PREFIX) + set(NodeJS_PREFIX "--prefix=${NodeJS_INSTALL_PREFIX}") else() - execute_process(COMMAND sh -c "./configure --with-icu-source=http://download.icu-project.org/files/icu4c/64.2/icu4c-64_2-src.tgz --shared" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}") + set(NodeJS_PREFIX) endif() + set(BUILD_DEBUG) + set(BUILD_DEBUG_ASAN) + set(BUILD_DEBUG_ASAN_OPTIONS) + + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(BUILD_DEBUG --debug) + + if(OPTION_BUILD_ADDRESS_SANITIZER) + set(BUILD_DEBUG_ASAN --enable-asan) + + if("${NodeJS_VERSION_MAJOR}" GREATER_EQUAL "20") + set(BUILD_DEBUG_ASAN_OPTIONS ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_container_overflow=0) + endif() + endif() + endif() + + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + COMMAND ${ICU_ENV_VAR} ./configure ${NodeJS_PREFIX} ${BUILD_ICU_FLAGS} --shared ${BUILD_DEBUG} ${BUILD_DEBUG_ASAN} + ) + message(STATUS "Build NodeJS shared library") + # Create build output directory + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${NodeJS_OUTPUT_PATH}/out) + include(ProcessorCount) ProcessorCount(N) - if(NOT N EQUAL 0) - execute_process(COMMAND sh -c "alias pyhton=`which python2.7`; make -j${N} -C out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}" OUTPUT_QUIET) + if(N GREATER 1) + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + COMMAND ${BUILD_DEBUG_ASAN_OPTIONS} make -j${N} -C ${NodeJS_OUTPUT_PATH}/out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1 + ) else() - execute_process(COMMAND sh -c "alias pyhton=`which python2.7`; make -C out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}" OUTPUT_QUIET) + execute_process( + WORKING_DIRECTORY "${NodeJS_OUTPUT_PATH}" + COMMAND ${BUILD_DEBUG_ASAN_OPTIONS} make -C ${NodeJS_OUTPUT_PATH}/out BUILDTYPE=${CMAKE_BUILD_TYPE} V=1 + ) endif() - - message(STATUS "Install NodeJS shared library") - - execute_process(COMMAND sh -c "make install" WORKING_DIRECTORY "${NODEJS_OUTPUT_PATH}" OUTPUT_QUIET) endif() endif() - # Find library - find_library(NODEJS_LIBRARY - NAMES ${NODEJS_LIBRARY_NAMES} - PATHS ${NODEJS_LIBRARY_PATH} + # Set up the compile path in case of prefix is specified + if(NOT WIN32 AND NOT MSVC AND NodeJS_INSTALL_PREFIX) + set(NodeJS_COMPILE_PREFIX_PATH "${NodeJS_INSTALL_PREFIX}/lib") + endif() + + # Find compiled library + find_library(NodeJS_LIBRARY + NAMES ${NodeJS_LIBRARY_NAMES} + PATHS ${NodeJS_COMPILE_PATH} ${NodeJS_COMPILE_PREFIX_PATH} DOC "NodeJS JavaScript Runtime Library" + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH ) + + if(NOT NodeJS_LIBRARY) + message(FATAL_ERROR "NodeJS library not found and it could not be built from source") + else() + set(NodeJS_BUILD_FROM_SOURCE TRUE) + endif() endif() -find_package_handle_standard_args(NODEJS - REQUIRED_VARS NODEJS_EXECUTABLE NODEJS_INCLUDE_DIR NODEJS_LIBRARY - VERSION_VAR NODEJS_VERSION -) +set(NodeJS_INCLUDE_DIRS "${NodeJS_INCLUDE_DIR}") + +if(NodeJS_V8_INCLUDE_DIR) + set(NodeJS_INCLUDE_DIRS "${NodeJS_INCLUDE_DIRS}" "${NodeJS_V8_INCLUDE_DIR}") +endif() + +if(NodeJS_UV_INCLUDE_DIR) + set(NodeJS_INCLUDE_DIRS "${NodeJS_INCLUDE_DIRS}" "${NodeJS_UV_INCLUDE_DIR}") +endif() -mark_as_advanced(NODEJS_EXECUTABLE NODEJS_INCLUDE_DIR NODEJS_LIBRARY) +if(NOT NodeJS_LIBRARY_NAME) + if(WIN32 AND MSVC) + get_filename_component(NodeJS_LIBRARY_NAME "${NodeJS_LIBRARY}" NAME_WLE) + set(NodeJS_LIBRARY_NAME "${NodeJS_LIBRARY_NAME}.dll") + else() + get_filename_component(NodeJS_LIBRARY_NAME "${NodeJS_LIBRARY}" NAME) + endif() +endif() -if(NODEJS_FOUND) - set(NODEJS_INCLUDE_DIRS ${NODEJS_INCLUDE_DIR}) +if(NOT NodeJS_LIBRARY_NAME_PATH AND WIN32) + string(REGEX REPLACE "[.]lib$" ".dll" NodeJS_LIBRARY_NAME_PATH ${NodeJS_LIBRARY}) endif() -if(_NODEJS_CMAKE_DEBUG) - message(STATUS "NODEJS_INCLUDE_DIR: ${NODEJS_INCLUDE_DIR}") - message(STATUS "NODEJS_VERSION: ${NODEJS_VERSION}") - message(STATUS "NODEJS_UV_VERSION: ${NODEJS_UV_VERSION}") - message(STATUS "NODEJS_V8_VERSION: ${NODEJS_V8_VERSION}") - message(STATUS "NODEJS_V8_VERSION_HEX: ${NODEJS_V8_VERSION_HEX}") - message(STATUS "NODEJS_LIBRARY: ${NODEJS_LIBRARY}") - message(STATUS "NODEJS_EXECUTABLE: ${NODEJS_EXECUTABLE}") +find_package_handle_standard_args(NodeJS + REQUIRED_VARS NodeJS_EXECUTABLE NodeJS_INCLUDE_DIRS NodeJS_LIBRARY NodeJS_LIBRARY_NAME + VERSION_VAR NodeJS_VERSION +) + +mark_as_advanced(NodeJS_EXECUTABLE NodeJS_INCLUDE_DIRS NodeJS_LIBRARY NodeJS_LIBRARY_NAME) + +if(NodeJS_CMAKE_DEBUG) + message(STATUS "NodeJS_INCLUDE_DIRS: ${NodeJS_INCLUDE_DIRS}") + message(STATUS "NodeJS_VERSION: ${NodeJS_VERSION}") + message(STATUS "NodeJS_UV_VERSION: ${NodeJS_UV_VERSION}") + message(STATUS "NodeJS_V8_VERSION: ${NodeJS_V8_VERSION}") + message(STATUS "NodeJS_V8_VERSION_HEX: ${NodeJS_V8_VERSION_HEX}") + message(STATUS "NodeJS_LIBRARY: ${NodeJS_LIBRARY}") + message(STATUS "NodeJS_EXECUTABLE: ${NodeJS_EXECUTABLE}") endif() diff --git a/cmake/FindPatchelf.cmake b/cmake/FindPatchelf.cmake new file mode 100644 index 0000000000..044cd412d0 --- /dev/null +++ b/cmake/FindPatchelf.cmake @@ -0,0 +1,32 @@ +# +# CMake Find Patchelf by Parra Studios +# CMake script to find Patchelf executable. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Prevent vervosity if already included +if(Patchelf_FOUND) + set(Patchelf_FIND_QUIETLY TRUE) +endif() + +# Find patchelf +find_program(Patchelf_EXECUTABLE NAMES patchelf) + +if(Patchelf_EXECUTABLE) + set(Patchelf_FOUND TRUE) +else() + set(Patchelf_FOUND FALSE) +endif() diff --git a/cmake/FindRapidJSON.cmake b/cmake/FindRapidJSON.cmake index 48f74af663..26b0d6b1e1 100644 --- a/cmake/FindRapidJSON.cmake +++ b/cmake/FindRapidJSON.cmake @@ -2,7 +2,7 @@ # CMake Find RapidJSON by Parra Studios # CMake script to find RapidJSON library. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindRubyEx.cmake b/cmake/FindRubyEx.cmake deleted file mode 100644 index 3fe57df8ac..0000000000 --- a/cmake/FindRubyEx.cmake +++ /dev/null @@ -1,175 +0,0 @@ -# -# CMake Find Ruby Engine by Parra Studios -# CMake script to find Ruby Engine. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Find Ruby library and include paths (Ruby 1.8, 1.9, 2.0, 2.1, 2.2, 2.3 and -# 2.4 are supported) -# -# RUBY_FOUND - True if Ruby was found -# RUBY_INCLUDE_DIRS - Ruby headers path -# RUBY_LIBRARY_DIRS - List of Ruby library directories -# RUBY_LIBRARY - List of Ruby libraries -# RUBY_VERSION - Ruby version -# RUBY_VERSION_MAJOR - Major Ruby version -# RUBY_VERSION_MINOR - Minor Ruby version -# RUBY_VERSION_PATCH - Patch Ruby version - - -if(RUBY_FOUND) - set(RUBY_FIND_QUIETLY TRUE) -endif() - -# Uncomment this option to debug -# set(_RUBY_CMAKE_DEBUG TRUE) - -set(RUBY_EXECUTABLE_NAMES - ruby2.4.0 ruby240 ruby2.4 ruby2.3.0 ruby230 ruby2.3 ruby23 ruby2.2.3 ruby223 - ruby2.2.2 ruby222 ruby2.2.1 ruby221 ruby2.2.0 ruby220 ruby2.2 ruby22 ruby2.1.7 ruby217 - ruby2.1.6 ruby216 ruby2.1.5 ruby215 ruby2.1.4 ruby214 ruby2.1.3 ruby213 - ruby2.1.2 ruby212 ruby2.1.1 ruby211 ruby2.1.0 ruby210 ruby2.1 ruby21 - ruby2.0 ruby20 ruby1.9.3 ruby193 ruby1.9.2 ruby192 ruby1.9.1 ruby191 ruby1.9 ruby19 - ruby1.8 ruby18 ruby -) - -find_program(RUBY_EXECUTABLE - NAMES ${RUBY_EXECUTABLE_NAMES} - PATHS /usr/bin /usr/local/bin /usr/pkg/bin -) - -if(RUBY_EXECUTABLE) - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['rubyhdrdir'] || RbConfig::CONFIG['archdir']" - OUTPUT_VARIABLE RUBY_INCLUDE_ARCH_DIR - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['oldincludedir']" - OUTPUT_VARIABLE RUBY_INCLUDE_OLD_DIR - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['arch']" - OUTPUT_VARIABLE RUBY_ARCH - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['libdir']" - OUTPUT_VARIABLE RUBY_POSSIBLE_LIB_PATH - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['rubylibdir']" - OUTPUT_VARIABLE RUBY_RUBY_LIB_PATH - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['MAJOR']" - OUTPUT_VARIABLE RUBY_VERSION_MAJOR - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['MINOR']" - OUTPUT_VARIABLE RUBY_VERSION_MINOR - ) - - execute_process( - COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['TEENY']" - OUTPUT_VARIABLE RUBY_VERSION_PATCH - ) - - set(RUBY_VERSION "${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}") - - set(RUBY_INCLUDE_OLD_ARCH_DIR - ${RUBY_INCLUDE_OLD_DIR}/${RUBY_ARCH} - ) - - set(RUBY_INCLUDE_VERSION_DIR - ${RUBY_INCLUDE_OLD_ARCH_DIR}/ruby-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH} - ) - - set(RUBY_INCLUDE_DIR - ${RUBY_INCLUDE_ARCH_DIR} - ${RUBY_INCLUDE_OLD_DIR} - ${RUBY_INCLUDE_OLD_ARCH_DIR} - ${RUBY_INCLUDE_VERSION_DIR} - ) - - find_path(RUBY_INCLUDE_MAIN_DIR - NAMES ruby.h - PATHS ${RUBY_INCLUDE_DIR} - ) - - find_path(RUBY_INCLUDE_CONFIG_DIR - NAMES ruby/config.h - PATHS ${RUBY_INCLUDE_DIR} - ) - - set(RUBY_INCLUDE_DIRS - ${RUBY_INCLUDE_MAIN_DIR} - ${RUBY_INCLUDE_CONFIG_DIR} - ) - - set(RUBY_LIBRARY_NAMES - ruby-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH} - ruby${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH} - ruby-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR} - ruby-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}-static - ruby - ruby-static - ) - - set(RUBY_LIBRARY_ARCH "${RUBY_POSSIBLE_LIB_PATH}/${RUBY_ARCH}") - - set(RUBY_LIBRARY_DIRS - ${RUBY_LIBRARY_ARCH} - ${RUBY_POSSIBLE_LIB_PATH} - ${RUBY_RUBY_LIB_PATH} - ) - - find_library(RUBY_LIBRARY - NAMES ${RUBY_LIBRARY_NAMES} - PATHS ${RUBY_LIBRARY_DIRS} - ) - - if(RUBY_LIBRARY AND RUBY_INCLUDE_DIRS) - set(RUBY_FOUND TRUE) - endif() - - mark_as_advanced( - RUBY_INCLUDE_DIRS - RUBY_LIBRARY_DIRS - RUBY_LIBRARY - RUBY_VERSION_MAJOR - RUBY_VERSION_MINOR - RUBY_VERSION_PATCH - RUBY_VERSION - ) - -endif() - -if(_RUBY_CMAKE_DEBUG) - message(STATUS "------------ FindRubyEx.cmake Debug ------------") - message(STATUS "RUBY_INCLUDE_DIRS = ${RUBY_INCLUDE_DIRS}") - message(STATUS "RUBY_LIBRARY_DIRS = ${RUBY_LIBRARY_DIRS}") - message(STATUS "RUBY_LIBRARY = ${RUBY_LIBRARY}") - message(STATUS "RUBY_VERSION_MAJOR = ${RUBY_VERSION_MAJOR}") - message(STATUS "RUBY_VERSION_MINOR = ${RUBY_VERSION_MINOR}") - message(STATUS "RUBY_VERSION_PATCH = ${RUBY_VERSION_PATCH}") - message(STATUS "RUBY_VERSION = ${RUBY_VERSION}") - message(STATUS "------------ FindRubyEx.cmake Debug ------------") -endif() diff --git a/cmake/FindRust.cmake b/cmake/FindRust.cmake new file mode 100644 index 0000000000..c9ef316aa2 --- /dev/null +++ b/cmake/FindRust.cmake @@ -0,0 +1,212 @@ +# +# CMake Find Rust by Parra Studios +# CMake script to find Rust compiler and tools. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Rust executables and paths +# +# Rust_FOUND - True if rust was found +# Rust_CARGO_HOME - Cargo home folder +# Rust_CARGO_EXECUTABLE - Cargo package manager executable path +# Rust_RUSTC_EXECUTABLE - Rust compiler executable path +# Rust_RUSTC_VERSION - Rust compiler vesion string +# Rust_RUSTC_SYSROOT - Rust compiler root location (includes binaries and libraries) +# Rust_RUSTC_LIBRARIES - Rust compiler runtime library list +# Rust_RUSTDOC_EXECUTABLE - Rust doc executable plath +# Rust_RUSTUP_EXECUTABLE - Rustup executable path +# Rust_GDB_EXECUTABLE - Rust GDB debugger executable path +# Rust_LLDB_EXECUTABLE - Rust LLDB debugger executable path +# Rust_TOOLCHAIN_TRIPLET - Current triplet used in rust compiler + +# Options +# +# Rust_CMAKE_DEBUG - Print the debug information and all constants values + +# Example for fixing the toolchain +# +# find_package(Rust COMPONENTS nightly-2021-10-09) + +option(Rust_CMAKE_DEBUG "Show full output of the Rust related commands for debugging." OFF) + +if(WIN32) + set(USER_HOME "$ENV{USERPROFILE}") +else() + set(USER_HOME "$ENV{HOME}") +endif() + +if(NOT DEFINED Rust_CARGO_HOME) + if("$ENV{Rust_CARGO_HOME}" STREQUAL "") + set(Rust_CARGO_HOME "${USER_HOME}/.cargo") + else() + set(Rust_CARGO_HOME "$ENV{Rust_CARGO_HOME}") + endif() +endif() + +set(Rust_CARGO_HOME "${Rust_CARGO_HOME}" CACHE PATH "Rust Cargo Home") + +set(Rust_PATHS + /usr + /usr/local + ${Rust_CARGO_HOME} +) + +find_program(Rust_RUSTUP_EXECUTABLE rustup + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +if(Rust_RUSTUP_EXECUTABLE AND Rust_FIND_COMPONENTS) + # Install the required toolchain (only one allowed by now) + list(GET Rust_FIND_COMPONENTS 0 Rust_TOOLCHAIN) + + if(Rust_TOOLCHAIN) + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} toolchain install ${Rust_TOOLCHAIN} --force + OUTPUT_VARIABLE Rust_OUTPUT + ERROR_VARIABLE Rust_OUTPUT + RESULT_VARIABLE Rust_STATUS + ) + + if(Rust_CMAKE_DEBUG) + message(STATUS "${Rust_OUTPUT}") + else() + if(Rust_STATUS AND NOT Rust_STATUS EQUAL 0) + message(FATAL_ERROR "${Rust_OUTPUT}") + endif() + endif() + + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} default ${Rust_TOOLCHAIN} + OUTPUT_VARIABLE Rust_OUTPUT + ERROR_VARIABLE Rust_OUTPUT + RESULT_VARIABLE Rust_STATUS + ) + + if(Rust_CMAKE_DEBUG) + message(STATUS "${Rust_OUTPUT}") + else() + if(Rust_STATUS AND NOT Rust_STATUS EQUAL 0) + message(FATAL_ERROR "${Rust_OUTPUT}") + endif() + endif() + + foreach(Rust_TOOLCHAIN_COMPONENT ${Rust_TOOLCHAIN_COMPONENT_LIST}) + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} toolchain install ${Rust_TOOLCHAIN} --component ${Rust_TOOLCHAIN_COMPONENT} + OUTPUT_VARIABLE Rust_OUTPUT + ERROR_VARIABLE Rust_OUTPUT + RESULT_VARIABLE Rust_STATUS + ) + + if(Rust_CMAKE_DEBUG) + message(STATUS "${Rust_OUTPUT}") + else() + if(Rust_STATUS AND NOT Rust_STATUS EQUAL 0) + message(FATAL_ERROR "${Rust_OUTPUT}") + endif() + endif() + endforeach() + + # Obtain toolchain full name and triplet + execute_process( + COMMAND ${Rust_RUSTUP_EXECUTABLE} default + OUTPUT_VARIABLE Rust_TOOLCHAIN_FULL_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + string(REPLACE " " ";" Rust_TOOLCHAIN_FULL_NAME ${Rust_TOOLCHAIN_FULL_NAME}) + list(GET Rust_TOOLCHAIN_FULL_NAME 0 Rust_TOOLCHAIN_FULL_NAME) + string(REPLACE "${Rust_TOOLCHAIN}-" "" Rust_TOOLCHAIN_TRIPLET ${Rust_TOOLCHAIN_FULL_NAME}) + endif() + +endif() + +find_program(Rust_CARGO_EXECUTABLE cargo + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_RUSTC_EXECUTABLE rustc + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_RUSTDOC_EXECUTABLE rustdoc + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_GDB_EXECUTABLE rust-gdb + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +find_program(Rust_LLDB_EXECUTABLE rust-lldb + HINTS ${Rust_PATHS} + PATH_SUFFIXES "bin" +) + +if(Rust_RUSTC_EXECUTABLE) + execute_process( + COMMAND ${Rust_RUSTC_EXECUTABLE} --version + OUTPUT_VARIABLE Rust_RUSTC_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE "rustc ([^ ]+) .*" "\\1" Rust_RUSTC_VERSION "${Rust_RUSTC_VERSION}") + + execute_process( + COMMAND ${Rust_RUSTC_EXECUTABLE} --print sysroot + OUTPUT_VARIABLE Rust_RUSTC_SYSROOT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + file( + GLOB Rust_RUSTC_LIBRARIES + ${Rust_RUSTC_SYSROOT}/lib/*${CMAKE_SHARED_LIBRARY_SUFFIX} + ) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Rust + FOUND_VAR Rust_FOUND + REQUIRED_VARS Rust_CARGO_EXECUTABLE Rust_RUSTC_EXECUTABLE Rust_RUSTC_LIBRARIES + VERSION_VAR Rust_RUSTC_VERSION +) + +mark_as_advanced( + Rust_FOUND + Rust_CARGO_EXECUTABLE + Rust_RUSTC_EXECUTABLE + Rust_RUSTC_SYSROOT + Rust_RUSTC_LIBRARIES + Rust_RUSTUP_EXECUTABLE + Rust_RUSTDOC_EXECUTABLE + Rust_GDB_EXECUTABLE + Rust_LLDB_EXECUTABLE +) + +if(Rust_CMAKE_DEBUG) + message(STATUS "Rust_CARGO_EXECUTABLE: ${Rust_CARGO_EXECUTABLE}") + message(STATUS "Rust_RUSTC_EXECUTABLE: ${Rust_RUSTC_EXECUTABLE}") + message(STATUS "Rust_RUSTC_SYSROOT: ${Rust_RUSTC_SYSROOT}") + message(STATUS "Rust_RUSTC_LIBRARIES: ${Rust_RUSTC_LIBRARIES}") + message(STATUS "Rust_RUSTUP_EXECUTABLE: ${Rust_RUSTUP_EXECUTABLE}") + message(STATUS "Rust_GDB_EXECUTABLE: ${Rust_GDB_EXECUTABLE}") + message(STATUS "Rust_LLDB_EXECUTABLE: ${Rust_LLDB_EXECUTABLE}") + message(STATUS "Rust_TOOLCHAIN_TRIPLET: ${Rust_TOOLCHAIN_TRIPLET}") +endif() diff --git a/cmake/FindSpiderMonkey.cmake b/cmake/FindSpiderMonkey.cmake index 8e7e881fcd..a952c9e4e9 100644 --- a/cmake/FindSpiderMonkey.cmake +++ b/cmake/FindSpiderMonkey.cmake @@ -2,7 +2,7 @@ # CMake Find Mozilla SpiderMonkey JavaScript Engine by Parra Studios # CMake script to find Mozilla SpiderMonkey Javascript Engine. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindV8.cmake b/cmake/FindV8.cmake index 7f9cbf1968..aedb8a8d7d 100644 --- a/cmake/FindV8.cmake +++ b/cmake/FindV8.cmake @@ -2,7 +2,7 @@ # CMake Find V8 Google JavaScript Engine by Parra Studios # CMake script to find V8 JavaScript Engine. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/FindWasmtime.cmake b/cmake/FindWasmtime.cmake new file mode 100644 index 0000000000..bf086293a9 --- /dev/null +++ b/cmake/FindWasmtime.cmake @@ -0,0 +1,173 @@ +# +# CMake Find Wasmtime WebAssembly Runtime by Parra Studios +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# + +# Find Wasmtime library and include paths +# +# Wasmtime_LIBRARY - Wasmtime shared library +# Wasmtime_INCLUDE_DIR - Wasmtime include directory + +option(Wasmtime_CMAKE_DEBUG "Show full output of the Wasmtime related commands for debugging." OFF) + +include(Portability) + +set(Wasmtime_VERSION ${Wasmtime_FIND_VERSION}) + +# See https://docs.wasmtime.dev/contributing-building.html#building-the-wasmtime-c-api +if(PROJECT_OS_LINUX) + set(Wasmtime_LIBRARY_NAME libwasmtime.so) + set(Wasmtime_PLATFORM "linux") + set(Wasmtime_ARCHIVE_EXTENSION "tar.xz") +elseif(PROJECT_OS_FAMILY STREQUAL "macos") + set(Wasmtime_LIBRARY_NAME libwasmtime.dylib) + set(Wasmtime_PLATFORM "macos") + set(Wasmtime_ARCHIVE_EXTENSION "tar.xz") +elseif(PROJECT_OS_WIN) + set(Wasmtime_LIBRARY_NAME wasmtime.lib) + set(Wasmtime_PLATFORM "windows") + set(Wasmtime_ARCHIVE_EXTENSION "zip") +elseif(PROJECT_OS_MINGW) + set(Wasmtime_LIBRARY_NAME wasmtime.a) + set(Wasmtime_PLATFORM "mingw") + set(Wasmtime_ARCHIVE_EXTENSION "zip") +else() + message(FATAL_ERROR "Could not determine target platform or target platform is not supported") +endif() + +if(PROJECT_ARCH_AMD64) + set(Wasmtime_ARCH "x86_64") +elseif(PROJECT_ARCH_AARCH64) + set(Wasmtime_ARCH "aarch64") +else() + set(Wasmtime_ARCH "x86_64") + message(WARNING "Could not determine target architecture, assuming x86_64") +endif() + +message(DEBUG "Set target architecture to ${Wasmtime_ARCH}") + +set(Wasmtime_LOCAL_PATH "${CMAKE_BINARY_DIR}/wasmtime") +set(Wasmtime_DOWNLOAD_DIR_NAME "wasmtime-v${Wasmtime_VERSION}-${Wasmtime_ARCH}-${Wasmtime_PLATFORM}-c-api") +set(Wasmtime_API_PATH "${Wasmtime_LOCAL_PATH}/${Wasmtime_DOWNLOAD_DIR_NAME}") +set(Wasmtime_DOWNLOAD_LIBRARY_PATH "${Wasmtime_API_PATH}/lib") +set(Wasmtime_DOWNLOAD_INCLUDE_DIR "${Wasmtime_API_PATH}/include") + +if(NOT Wasmtime_LIBRARY) + set(Wasmtime_DEFAULT_LIBRARY_PATHS + /usr/lib + ) + find_library(Wasmtime_LIBRARY + NAMES ${Wasmtime_LIBRARY_NAME} + DOC "Wasmtime C API library" + ) + + if(Wasmtime_LIBRARY) + if(PROJECT_OS_WIN) + string(REGEX REPLACE "[.]lib$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + elseif(PROJECT_OS_MINGW) + string(REGEX REPLACE "[.]a$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + endif() + endif() +endif() + +if(NOT Wasmtime_INCLUDE_DIR) + set(Wasmtime_DEFAULT_INCLUDE_PATHS + /usr/include + ) + find_path(Wasmtime_INCLUDE_DIR + NAMES wasm.h wasmtime.h + DOC "Wasmtime C API headers" + ) +endif() + +if(NOT Wasmtime_LIBRARY OR NOT Wasmtime_INCLUDE_DIR) + message(STATUS "Wasmtime C API library or headers not found, downloading from archive") + + if(Wasmtime_ARCH STREQUAL "x86_64" AND PROJECT_ARCH_32BIT) + # We assumed target architecture was x86_64, but it is 32-bit, so the + # assumption is definitely invalid. + message(FATAL_ERROR "No downloads available for target architecture, please install Wasmtime manually") + endif() + + set(Wasmtime_DOWNLOAD_ARCHIVE_NAME "${Wasmtime_DOWNLOAD_DIR_NAME}.${Wasmtime_ARCHIVE_EXTENSION}") + set(Wasmtime_DOWNLOAD_URL + "https://github.com/bytecodealliance/wasmtime/releases/download/v$%7BWasmtime_VERSION%7D/$%7BWasmtime_DOWNLOAD_ARCHIVE_NAME%7D") + set(Wasmtime_DOWNLOAD_DEST "${Wasmtime_LOCAL_PATH}/${Wasmtime_DOWNLOAD_ARCHIVE_NAME}") + + file(DOWNLOAD ${Wasmtime_DOWNLOAD_URL} ${Wasmtime_DOWNLOAD_DEST} + STATUS DOWNLOAD_STATUS + SHOW_PROGRESS) + + list(GET DOWNLOAD_STATUS 0 STATUS_CODE) + + if(NOT STATUS_CODE EQUAL 0) + list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE) + message(FATAL_ERROR "Failed to download Wasmtime C API: ${ERROR_MESSAGE}") + endif() + + file(ARCHIVE_EXTRACT + INPUT ${Wasmtime_DOWNLOAD_DEST} + DESTINATION ${Wasmtime_LOCAL_PATH} + ) + + if(NOT Wasmtime_LIBRARY) + find_library(Wasmtime_LIBRARY + NAMES ${Wasmtime_LIBRARY_NAME} + PATHS ${Wasmtime_DOWNLOAD_LIBRARY_PATH} + NO_DEFAULT_PATH + DOC "Wasmtime C API library" + ) + + if(Wasmtime_LIBRARY) + set(Wasmtime_LIBRARY_INSTALLED ON) + + if(PROJECT_OS_WIN) + string(REGEX REPLACE "[.]lib$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + elseif(PROJECT_OS_MINGW) + string(REGEX REPLACE "[.]a$" ".dll" Wasmtime_LIBRARY_DLL ${Wasmtime_LIBRARY}) + endif() + endif() + endif() + + if(NOT Wasmtime_INCLUDE_DIR) + find_path(Wasmtime_INCLUDE_DIR + NAMES wasm.h wasmtime.h + PATHS ${Wasmtime_DOWNLOAD_INCLUDE_DIR} + NO_DEFAULT_PATH + DOC "Wasmtime C API headers" + ) + endif() +endif() + +# Copy Wasmtime DLL to the output directory +if(Wasmtime_LIBRARY_DLL) + file(COPY "${Wasmtime_LIBRARY_DLL}" DESTINATION "${PROJECT_OUTPUT_DIR}") + set(Wasmtime_LIBRARY_DEPENDENCIES + ws2_32.lib + advapi32.lib + userenv.lib + ntdll.lib + shell32.lib + ole32.lib + bcrypt.lib + ) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Wasmtime + FOUND_VAR Wasmtime_FOUND + REQUIRED_VARS Wasmtime_LIBRARY Wasmtime_INCLUDE_DIR + VERSION_VAR Wasmtime_VERSION +) + +mark_as_advanced( + Wasmtime_FOUND + Wasmtime_LIBRARY + Wasmtime_INCLUDE_DIR +) + +if(Wasmtime_CMAKE_DEBUG) + message(STATUS "Found Wasmtime C API library at ${Wasmtime_LIBRARY}") + message(STATUS "Found Wasmtime C API header in ${Wasmtime_INCLUDE_DIR}") +endif() diff --git a/cmake/FindZig.cmake b/cmake/FindZig.cmake new file mode 100644 index 0000000000..cb2764032c --- /dev/null +++ b/cmake/FindZig.cmake @@ -0,0 +1,47 @@ +# +# CMake Find Zig by Parra Studios +# CMake script to find Zig compiler and tools. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Find Zig executables and paths +# +# Zig_FOUND - True if Zig was found +# Zig_COMPILER_EXECUTABLE - Zig compiler executable path + +# Options +# +# Zig_CMAKE_DEBUG - Print the debug information and all constants values + +option(Zig_CMAKE_DEBUG "Show full output of the Zig related commands for debugging." OFF) + +find_program(Zig_COMPILER_EXECUTABLE zig +) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Zig + FOUND_VAR Zig_FOUND +) + +mark_as_advanced( + Zig_FOUND + Zig_COMPILER_EXECUTABLE +) + +if(Zig_CMAKE_DEBUG) + message(STATUS "Zig_COMPILER_EXECUTABLE: ${Zig_COMPILER_EXECUTABLE}") +endif() diff --git a/cmake/InstallGBench.cmake b/cmake/InstallGBench.cmake index d3cbd1a3b8..ccf3073d7a 100644 --- a/cmake/InstallGBench.cmake +++ b/cmake/InstallGBench.cmake @@ -2,7 +2,7 @@ # CMake Install Google Benchmark by Parra Studios # CMake script to install Google Benchmark library. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,15 +24,15 @@ if(NOT GBENCH_FOUND OR USE_BUNDLED_GBENCH) if(NOT GBENCH_VERSION OR USE_BUNDLED_GBENCH) - set(GBENCH_VERSION 1.4.1) - set(GBENCH_URL_MD5 482dddb22bec43f5507a000456d6bb88) + set(GBENCH_VERSION 1.6.1) + set(GBENCH_URL_MD5 8c33c51f9b7154e6c290df3750081c87) endif() ExternalProject_Add(google-bench-depends DOWNLOAD_NAME GBench-${GBENCH_VERSION}.tar.gz URL https://github.com/google/benchmark/archive/v${GBENCH_VERSION}.tar.gz URL_MD5 ${GBENCH_URL_MD5} - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_ENABLE_TESTING=OFF + CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_ENABLE_TESTING=OFF TEST_COMMAND "" ) diff --git a/cmake/InstallGTest.cmake b/cmake/InstallGTest.cmake index 87a9f3c717..45fdf569c9 100644 --- a/cmake/InstallGTest.cmake +++ b/cmake/InstallGTest.cmake @@ -2,7 +2,7 @@ # CMake Install Google Test by Parra Studios # CMake script to install Google Test library. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) if(NOT GTEST_VERSION OR USE_BUNDLED_GTEST) - set(GTEST_VERSION 1.8.1) + set(GTEST_VERSION 1.16.0) endif() find_package(Threads REQUIRED) @@ -35,11 +35,23 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) set(GTEST_DISABLE_PTHREADS OFF) endif() + if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + if(OPTION_BUILD_THREAD_SANITIZER) + set(SANITIZER_FLAGS -DCMAKE_CXX_FLAGS=/fsanitize=thread -DCMAKE_C_FLAGS=/fsanitize=thread) + endif() + if(OPTION_BUILD_ADDRESS_SANITIZER) + set(SANITIZER_FLAGS -DCMAKE_CXX_FLAGS=/fsanitize=address -DCMAKE_C_FLAGS=/fsanitize=address) + endif() + if(OPTION_BUILD_MEMORY_SANITIZER) + set(SANITIZER_FLAGS -DCMAKE_CXX_FLAGS="/fsanitize=memory /fsanitize=leak" -DCMAKE_C_FLAGS="/fsanitize=memory /fsanitize=leak") + endif() + endif() + # Import Google Test Framework ExternalProject_Add(google-test-depends GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-${GTEST_VERSION} - CMAKE_ARGS -Dgmock_build_tests=OFF + GIT_TAG v${GTEST_VERSION} + CMAKE_ARGS -Dgtest_build_samples=OFF -Dgtest_build_tests=OFF -Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS} @@ -47,6 +59,8 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) -Dgtest_hide_internal_symbols=OFF -DINSTALL_GTEST=OFF -DBUILD_GMOCK=ON + -Dgmock_build_tests=OFF + ${SANITIZER_FLAGS} PREFIX "${CMAKE_CURRENT_BINARY_DIR}" UPDATE_COMMAND "" INSTALL_COMMAND "" @@ -57,24 +71,18 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) ExternalProject_Get_Property(google-test-depends source_dir binary_dir) set(GTEST_INCLUDE_DIR "${source_dir}/googletest/include") - set(GMOCK_INCLUDE_DIR "${source_dir}/googlemock/include") + set(GMOCK_INCLUDE_DIR "${source_dir}/googlemock/include") if(MSVC) set(GTEST_LIB_PREFIX "") - set(GTEST_LIB_SUFFIX "lib") - set(GTEST_LIBS_DIR "${binary_dir}/googlemock/gtest/${CMAKE_BUILD_TYPE}") - set(GMOCK_LIBS_DIR "${binary_dir}/googlemock/${CMAKE_BUILD_TYPE}") - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(GTEST_LIB_DEBUG "d") - else() - set(GTEST_LIB_DEBUG "") - endif() + set(GTEST_LIB_SUFFIX "lib") + set(GTEST_LIBS_DIR "${binary_dir}/lib/${CMAKE_BUILD_TYPE}") + set(GMOCK_LIBS_DIR "${binary_dir}/lib/${CMAKE_BUILD_TYPE}") else() - set(GTEST_LIB_PREFIX "lib") - set(GTEST_LIB_SUFFIX "a") - set(GTEST_LIBS_DIR "${binary_dir}/googlemock/gtest") - set(GMOCK_LIBS_DIR "${binary_dir}/googlemock") - set(GTEST_LIB_DEBUG "") + set(GTEST_LIB_PREFIX "lib") + set(GTEST_LIB_SUFFIX "a") + set(GTEST_LIBS_DIR "${binary_dir}/lib") + set(GMOCK_LIBS_DIR "${binary_dir}/lib") endif() # Define Paths @@ -83,15 +91,30 @@ if(NOT GTEST_FOUND OR USE_BUNDLED_GTEST) "${GMOCK_INCLUDE_DIR}" ) + set(GTEST_LIBRARY + "${GTEST_LIBS_DIR}/${GTEST_LIB_PREFIX}gtest.${GTEST_LIB_SUFFIX}" + ) + + set(GMOCK_LIBRARY + "${GMOCK_LIBS_DIR}/${GTEST_LIB_PREFIX}gmock.${GTEST_LIB_SUFFIX}" + ) + set(GTEST_LIBRARIES - "${GTEST_LIBS_DIR}/${GTEST_LIB_PREFIX}gtest${GTEST_LIB_DEBUG}.${GTEST_LIB_SUFFIX}" - "${GMOCK_LIBS_DIR}/${GTEST_LIB_PREFIX}gmock${GTEST_LIB_DEBUG}.${GTEST_LIB_SUFFIX}" + "${GTEST_LIBRARY}" + "${GMOCK_LIBRARY}" "${CMAKE_THREAD_LIBS_INIT}" ) set(GTEST_FOUND TRUE) - mark_as_advanced(GTEST_INCLUDE_DIRS GTEST_LIBRARIES) + mark_as_advanced( + GTEST_LIBRARY + GMOCK_LIBRARY + GTEST_LIBRARIES + GTEST_INCLUDE_DIR + GMOCK_INCLUDE_DIR + GTEST_INCLUDE_DIRS + ) message(STATUS "Install Google Test v${GTEST_VERSION}") -endif () +endif() diff --git a/cmake/InstallLibTCC.cmake b/cmake/InstallLibTCC.cmake new file mode 100644 index 0000000000..23a780e264 --- /dev/null +++ b/cmake/InstallLibTCC.cmake @@ -0,0 +1,191 @@ +# +# CMake Install Tiny C Compiler by Parra Studios +# CMake script to install TCC library. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# The following variables are set: +# +# LIBTCC_FOUND - True if TCC library was found +# LIBTCC_TARGET - Defines the TCC depends target (for using it as dependency on other targets) +# LIBTCC_INCLUDE_DIR - TCC headers path +# LIBTCC_LIBRARY - List of TCC libraries + +if(LIBTCC_FOUND) + return() +endif() + +include(Portability) + +set(LIBTCC_INSTALL_PREFIX "${PROJECT_OUTPUT_DIR}/libtcc") +file(MAKE_DIRECTORY ${LIBTCC_INSTALL_PREFIX}) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + set(LIBTCC_DEBUG "--debug") +else() + set(LIBTCC_DEBUG) +endif() + +# Configure +if(PROJECT_OS_FAMILY STREQUAL unix) + if(OPTION_BUILD_MUSL) + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG} --disable-static --config-musl) + else() + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG} --disable-static --with-selinux) + endif() +elseif(PROJECT_OS_FAMILY STREQUAL macos) + # TODO: --disable-static is not working on MacOS, this should be reported or further investigated + + # AddressSanitizer:DEADLYSIGNAL + # ================================================================= + # ==5339==ERROR: AddressSanitizer: BUS on unknown address 0x7fffac377b10 (pc 0x7fffac377b10 bp 0x7ffee8c2a0a0 sp 0x7ffee8c29f98 T0) + # #0 0x7fffac377b0f in off32 (libsystem_c.dylib:x86_64+0x3647db0f) + # #1 0x10bab502d in parse_btype tccgen.c + # #2 0x10babcd02 in decl0 tccgen.c:8197 + # #3 0x10baa74d8 in tccgen_compile tccgen.c:8449 + # #4 0x10ba90c48 in tcc_compile libtcc.c:742 + # #5 0x10ba9186a in tcc_add_file_internal libtcc.c:1101 + # #6 0x10b9e8db1 in c_loader_impl_load_from_file c_loader_impl.cpp:933 + # #7 0x1071b8fc5 in loader_impl_load_from_file loader_impl.c:842 + # #8 0x1071b302d in loader_load_from_file loader.c:317 + # #9 0x1071c9488 in metacall_load_from_file metacall.c:348 + # #10 0x106fd7ce1 in metacall_c_test_DefaultConstructor_Test::TestBody() metacall_c_test.cpp:53 + # #11 0x10704125d in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-c-testd:x86_64+0x10006d25d) + # #12 0x107002e9a in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-c-testd:x86_64+0x10002ee9a) + # #13 0x107002dd2 in testing::Test::Run() (metacall-c-testd:x86_64+0x10002edd2) + # #14 0x107004011 in testing::TestInfo::Run() (metacall-c-testd:x86_64+0x100030011) + # #15 0x1070051ff in testing::TestSuite::Run() (metacall-c-testd:x86_64+0x1000311ff) + # #16 0x107014ce5 in testing::internal::UnitTestImpl::RunAllTests() (metacall-c-testd:x86_64+0x100040ce5) + # #17 0x10704570d in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (metacall-c-testd:x86_64+0x10007170d) + # #18 0x10701465a in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (metacall-c-testd:x86_64+0x10004065a) + # #19 0x10701452f in testing::UnitTest::Run() (metacall-c-testd:x86_64+0x10004052f) + # #20 0x106fd6049 in RUN_ALL_TESTS() gtest.h:2490 + # #21 0x106fd5ee3 in main main.cpp:27 + # #22 0x7fff75eb03d4 in start (libdyld.dylib:x86_64+0x163d4) + + # ==5339==Register values: + # rax = 0x000000000000002c rbx = 0x0000000000000001 rcx = 0x00006250000062a0 rdx = 0x0000000000000000 + # rdi = 0x000000000000002c rsi = 0x00007ffee8c2a028 rbp = 0x00007ffee8c2a0a0 rsp = 0x00007ffee8c29f98 + # r8 = 0x000000010bb0f350 r9 = 0x0000000111c5457c r10 = 0x0000000000000000 r11 = 0x00007fffac377b10 + # r12 = 0x0000000000000000 r13 = 0x00007ffee8c29fe0 r14 = 0x000000010bb13c50 r15 = 0x000000000000053b + # AddressSanitizer can not provide additional info. + # SUMMARY: AddressSanitizer: BUS (libsystem_c.dylib:x86_64+0x3647db0f) in off32 + + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG}) # --disable-static +elseif(PROJECT_OS_FAMILY STREQUAL win32) + if(PROJECT_OS_NAME STREQUAL MinGW) + set(LIBTCC_CONFIGURE ./configure --prefix=${LIBTCC_INSTALL_PREFIX} ${LIBTCC_DEBUG} --config-mingw32 --disable-static) + else() + set(LIBTCC_CONFIGURE "") + endif() +else() + message(FATAL_ERROR "TCC library install support not implemented in this platform") +endif() + +include(ProcessorCount) +ProcessorCount(N) + +# Build +if(PROJECT_OS_BSD) + set(LIBTCC_BUILD gmake -j${N}) +elseif(PROJECT_OS_FAMILY STREQUAL unix) + set(LIBTCC_BUILD make -j${N}) +elseif(PROJECT_OS_FAMILY STREQUAL macos) + set(LIBTCC_BUILD make -j${N} MACOSX_DEPLOYMENT_TARGET=${PROJECT_OS_VERSION}) +elseif(PROJECT_OS_FAMILY STREQUAL win32) + if(PROJECT_OS_NAME STREQUAL MinGW) + set(LIBTCC_BUILD make -j${N}) + else() + set(LIBTCC_BUILD ./win32/build-tcc.bat -i ${LIBTCC_INSTALL_PREFIX}) + endif() +else() + message(FATAL_ERROR "TCC library install support not implemented in this platform") +endif() + +# Install +if(PROJECT_OS_BSD) + set(LIBTCC_INSTALL gmake install) +elseif(PROJECT_OS_FAMILY STREQUAL win32 AND PROJECT_OS_NAME STREQUAL Windows) + set(LIBTCC_INSTALL "") +else() + set(LIBTCC_INSTALL make install) +endif() + +set(LIBTCC_TARGET libtcc-depends) +set(LIBTCC_COMMIT_SHA "6ec4a10") +if(PROJECT_OS_FAMILY STREQUAL macos) + # TODO: --disable-static is not working on MacOS, this should be reported or further investigated, remove this when it is solved + set(LIBTTC_LIBRARY_NAME "${CMAKE_STATIC_LIBRARY_PREFIX}tcc${CMAKE_STATIC_LIBRARY_SUFFIX}") +else() + set(LIBTTC_LIBRARY_NAME "${CMAKE_SHARED_LIBRARY_PREFIX}tcc${CMAKE_SHARED_LIBRARY_SUFFIX}") +endif() +set(LIBTTC_LIBRARY_PATH "${PROJECT_OUTPUT_DIR}/${LIBTTC_LIBRARY_NAME}") +set(LIBTTC_RUNTIME_PATH "${LIBTCC_INSTALL_PREFIX}/lib/tcc") +set(LIBTTC_RUNTIME_INCLUDE_PATH "${LIBTTC_RUNTIME_PATH}/include") +set(LIBTTC_RUNTIME_FILES + "${LIBTTC_RUNTIME_PATH}/libtcc1.a" + "${LIBTTC_RUNTIME_PATH}/bcheck.o" + "${LIBTTC_RUNTIME_PATH}/bt-exe.o" + "${LIBTTC_RUNTIME_PATH}/bt-log.o" +) + +# LibTCC Proejct +ExternalProject_Add(${LIBTCC_TARGET} + DOWNLOAD_NAME tinycc.tar.gz + URL https://github.com/metacall/tinycc/archive/${LIBTCC_COMMIT_SHA}.tar.gz + URL_MD5 1d25d1a07a39c6d6671b7221d5286dc1 + CONFIGURE_COMMAND ${LIBTCC_CONFIGURE} + BUILD_COMMAND ${LIBTCC_BUILD} + BUILD_IN_SOURCE true + TEST_COMMAND "" + UPDATE_COMMAND "" + INSTALL_COMMAND ${LIBTCC_INSTALL} + COMMAND ${CMAKE_COMMAND} -E copy "${LIBTCC_INSTALL_PREFIX}/lib/${LIBTTC_LIBRARY_NAME}" "${LIBTTC_LIBRARY_PATH}" + COMMAND ${CMAKE_COMMAND} -E copy ${LIBTTC_RUNTIME_FILES} "${PROJECT_OUTPUT_DIR}" +) + +# Install Library +install(FILES + ${LIBTTC_LIBRARY_PATH} + DESTINATION ${INSTALL_LIB} + COMPONENT runtime +) + +# Runtime files +install(DIRECTORY + ${LIBTTC_RUNTIME_PATH}/ + DESTINATION ${INSTALL_LIB} + COMPONENT runtime + FILES_MATCHING + PATTERN "*.a" + PATTERN "*.o" + PATTERN "include" EXCLUDE +) + +# Header files +install(DIRECTORY + ${LIBTTC_RUNTIME_INCLUDE_PATH}/ + DESTINATION ${INSTALL_INCLUDE} + COMPONENT runtime +) + +set(LIBTCC_INCLUDE_DIR "${LIBTCC_INSTALL_PREFIX}/include") +set(LIBTCC_LIBRARY "${LIBTTC_LIBRARY_PATH}") +set(LIBTCC_FOUND TRUE) + +mark_as_advanced(LIBTCC_INCLUDE_DIR LIBTCC_LIBRARY) + +message(STATUS "Installing LibTCC ${LIBTCC_COMMIT_SHA}") diff --git a/cmake/InstallPatchelf.cmake b/cmake/InstallPatchelf.cmake new file mode 100644 index 0000000000..72b44a952d --- /dev/null +++ b/cmake/InstallPatchelf.cmake @@ -0,0 +1,57 @@ +# +# CMake Find Patchelf by Parra Studios +# CMake script to find Patchelf executable. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(Patchelf_FOUND) + return() +endif() + +if(WIN32 OR APPLE) + # TODO: Download binaries (https://github.com/NixOS/patchelf/releases/tag/0.18.0) + message(WARNING "Patchelf not supported in MacOs or Windows") +endif() + +set(Patchelf_PREFIX_DIR "${CMAKE_BINARY_DIR}/Patchelf") +set(Patchelf_SOURCE_DIR "${Patchelf_PREFIX_DIR}/src/patchelf") +set(Patchelf_INSTALL_DIR "${Patchelf_PREFIX_DIR}/install/patchelf") +set(Patchelf_STAMP_DIR "${Patchelf_PREFIX_DIR}/stamp/patchelf") +set(Patchelf_TMP_DIR "${Patchelf_PREFIX_DIR}/tmp/patchelf") + +include(ExternalProject) + +ExternalProject_Add(Patchelf + PREFIX "${Patchelf_PREFIX_DIR}" + GIT_REPOSITORY "https://github.com/NixOS/patchelf" + GIT_TAG "0.18.0" + PATCH_COMMAND "" + SOURCE_DIR "${Patchelf_SOURCE_DIR}" + BINARY_DIR "${Patchelf_SOURCE_DIR}" + INSTALL_DIR "${Patchelf_INSTALL_DIR}" + STAMP_DIR "${Patchelf_STAMP_DIR}" + TMP_DIR "${Patchelf_TMP_DIR}" + CONFIGURE_COMMAND ${Patchelf_SOURCE_DIR}/bootstrap.sh + BUILD_COMMAND ${Patchelf_SOURCE_DIR}/configure + INSTALL_COMMAND make + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 + LOG_INSTALL 1 +) + +set(Patchelf_EXECUTABLE "${CMAKE_BINARY_DIR}/Patchelf/src/patchelf/src/patchelf") +set(Patchelf_FOUND TRUE) diff --git a/cmake/InstallRapidJSON.cmake b/cmake/InstallRapidJSON.cmake index 56131aa81c..f90dfc4a4e 100644 --- a/cmake/InstallRapidJSON.cmake +++ b/cmake/InstallRapidJSON.cmake @@ -2,7 +2,7 @@ # CMake Install RapidJSON by Parra Studios # CMake script to install RapidJSON library. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,29 +24,25 @@ if(NOT RAPIDJSON_FOUND OR USE_BUNDLED_RAPIDJSON) if(NOT RAPIDJSON_VERSION OR USE_BUNDLED_RAPIDJSON) - set(RAPIDJSON_VERSION 1.1.0) - set(RAPIDJSON_URL_MD5 badd12c511e081fec6c89c43a7027bce) + set(RAPIDJSON_VERSION 24b5e7a8b27f42fa16b96fc70aade9106cf7102f) endif() ExternalProject_Add(rapid-json-depends - DOWNLOAD_NAME RapidJSON-${RAPIDJSON_VERSION}.tar.gz - URL https://github.com/miloyip/rapidjson/archive/v${RAPIDJSON_VERSION}.tar.gz - URL_MD5 ${RAPIDJSON_URL_MD5} - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX= - -DRAPIDJSON_BUILD_DOC=Off - -DRAPIDJSON_BUILD_EXAMPLES=Off - -DRAPIDJSON_BUILD_TESTS=Off - TEST_COMMAND "" + GIT_REPOSITORY "https://github.com/Tencent/rapidjson.git" + GIT_TAG "${RAPIDJSON_VERSION}" + BUILD_COMMAND "" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" ) - ExternalProject_Get_Property(rapid-json-depends INSTALL_DIR) + ExternalProject_Get_Property(rapid-json-depends SOURCE_DIR) - set(RAPIDJSON_ROOT_DIR ${INSTALL_DIR}) + set(RAPIDJSON_ROOT_DIR ${SOURCE_DIR}) set(RAPIDJSON_INCLUDE_DIRS ${RAPIDJSON_ROOT_DIR}/include) set(RAPIDJSON_FOUND TRUE) mark_as_advanced(RAPIDJSON_INCLUDE_DIRS) message(STATUS "Installing RapidJSON v${RAPIDJSON_VERSION}") -endif () +endif() diff --git a/cmake/NodeJSGYPPatch.py b/cmake/NodeJSGYPPatch.py new file mode 100644 index 0000000000..ec82a50edb --- /dev/null +++ b/cmake/NodeJSGYPPatch.py @@ -0,0 +1,54 @@ +from sys import argv +from os.path import isfile +from shutil import copy2 + +# Validate arguments +if len(argv) != 2: + print('Invalid number of arguments, you should pass the location of \'node.gyp\'.') + print('Usage: python3 NodeJSGYPPatch.py ') + exit(1) + +# Validate the project file +if not isfile(argv[1]) or not argv[1].endswith('node.gyp'): + print('The file \'' + argv[1] + '\' does not exist or is not a valid gyp file, it must be named as \'node.gyp\'.') + exit(2) + +# Read the file +f = open(argv[1]) +nodegyp = eval(f.read()) +f.close() + +# Validate that the target is present and it is correct +target = next((x for x in nodegyp['targets'] if 'target_name' in x and x['target_name'] == '<(node_lib_target_name)'), None) + +if target is None: + print('Invalid node.gyp configuration, the target \'node_lib_target_name\' is not present.') + exit(3) + +condition = next((x for x in target['conditions'] if x[0] == 'OS=="win"'), None) + +if condition is None or (condition is not None and len(condition) != 2): + print('Invalid node.gyp configuration, the condition \'OS=="win"\' is not present in target \'node_lib_target_name\'.') + exit(4) + +if not 'libraries' in condition[1]: + print('Invalid node.gyp configuration, \'libraries\' field is not present in the condition \'OS=="win"\' of the target target \'node_lib_target_name\'.') + exit(5) + +# Get the libraries +libraries = condition[1]['libraries'] + +# Check if the library to patch is present +if not 'Winmm' in libraries: + # Copy file as backup + copy2(argv[1], argv[1] + '.backup') + + # Apply the patch to the libraries + libraries.append('Winmm') + + # Overwrite the node.gyp project + f = open(argv[1], 'w') + f.write(repr(nodegyp)) + f.close() + +print('Build project node.gyp patched correctly') diff --git a/cmake/Portability.cmake b/cmake/Portability.cmake index 75f6d1f9bf..556427b187 100644 --- a/cmake/Portability.cmake +++ b/cmake/Portability.cmake @@ -2,7 +2,7 @@ # Portability CMake support by Parra Studios # Cross-platform and architecture detection utility. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,6 +54,36 @@ if(PROJECT_OS_SOLARIS) set(PROJECT_OS_FAMILY unix) endif() +# Check Android +if(ANDROID) + set(PROJECT_OS_ANDROID TRUE BOOL INTERNAL) + set(PROJECT_OS_NAME "Android") + set(PROJECT_OS_FAMILY unix) +endif() + +# Check Haiku +string(REGEX MATCH "Haiku" PROJECT_OS_HAIKU ${CMAKE_SYSTEM_NAME}) + +if(PROJECT_OS_HAIKU) + set(HAIKU 1) + set(PROJECT_OS_HAIKU TRUE BOOL INTERNAL) + set(PROJECT_OS_NAME "Haiku") + set(PROJECT_OS_FAMILY beos) + add_compile_definitions(__HAIKU__) + + # Workaround to enable Haiku with export headers + # This can be removed once export headers support Haiku + if(PROJECT_OS_HAIKU) + # As the function is already patched, repatch it again + function(_GENERATE_EXPORT_HEADER) + set(WIN32 1) + # When the function is redefined, the old function can be accessed through underscore + __GENERATE_EXPORT_HEADER(${ARGN}) + unset(WIN32) + endfunction() + endif() +endif() + # Check Windows if(WIN32) set(PROJECT_OS_WIN TRUE BOOL INTERNAL) @@ -61,6 +91,13 @@ if(WIN32) set(PROJECT_OS_FAMILY win32) endif() +# Check MinGW +if(MINGW) + set(PROJECT_OS_MINGW TRUE BOOL INTERNAL) + set(PROJECT_OS_NAME "MinGW") + set(PROJECT_OS_FAMILY win32) +endif() + # Check Apple OS if(APPLE) # Check if it's OS X or another MacOS @@ -76,8 +113,12 @@ if(APPLE) set(PROJECT_OS_FAMILY macos) - exec_program(uname ARGS -v OUTPUT_VARIABLE PROJECT_OS_VERSION) - string(REGEX MATCH "[0-9]+" PROJECT_OS_VERSION ${PROJECT_OS_VERSION}) + # Retrieve MacOs version from sv_vers + exec_program(sw_vers OUTPUT_VARIABLE SW_VERS_OUTPUT) + string(REPLACE "\n" ";" SW_VERS_OUTPUT "${SW_VERS_OUTPUT}") + string(REPLACE ":" ";" SW_VERS_OUTPUT "${SW_VERS_OUTPUT}") + list(GET SW_VERS_OUTPUT 3 PROJECT_OS_VERSION) + string(STRIP "${PROJECT_OS_VERSION}" PROJECT_OS_VERSION) endif() # Check QNX @@ -105,18 +146,22 @@ set(PROJECT_ARCH_NAME ${CMAKE_SYSTEM_PROCESSOR}) # 32 or 64 bit Linux if(PROJECT_OS_LINUX) - if(${PROJECT_ARCH_NAME} STREQUAL "x86") + if (${PROJECT_ARCH_NAME} MATCHES "^(x86|i[3-6]86|x86_64)$" AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") set(PROJECT_ARCH_32BIT TRUE BOOL INTERNAL) - endif() - - if(${PROJECT_ARCH_NAME} STREQUAL "x86_64") + set(PROJECT_ARCH_X86 TRUE BOOL INTERNAL) + elseif(${PROJECT_ARCH_NAME} MATCHES "(x86_64)|(amd64)|(AMD64)") set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_AMD64 TRUE BOOL INTERNAL) + elseif(${PROJECT_ARCH_NAME} STREQUAL "aarch64") + set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_AARCH64 TRUE BOOL INTERNAL) elseif(${PROJECT_ARCH_NAME} STREQUAL "ppc64") set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) elseif(${PROJECT_ARCH_NAME} STREQUAL "s390x") - set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) endif() + # Verify the architecture is correct if(PROJECT_ARCH_32BIT) if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") message(STATUS "Linux ${PROJECT_ARCH_NAME} 32bit detected") @@ -138,7 +183,7 @@ endif() if(NOT PROJECT_ARCH_32BIT AND NOT PROJECT_ARCH_64BIT) if("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") message(STATUS "32bit architecture ${PROJECT_ARCH_NAME} detected") - set(PROJECT_ARCH_32BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_32BIT TRUE BOOL INTERNAL) if(PROJECT_OS_WIN) set(WINXBITS Win32) @@ -146,7 +191,7 @@ if(NOT PROJECT_ARCH_32BIT AND NOT PROJECT_ARCH_64BIT) elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") message(STATUS "64bit architecture ${PROJECT_ARCH_NAME} detected") - set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) + set(PROJECT_ARCH_64BIT TRUE BOOL INTERNAL) if(PROJECT_OS_WIN) set(WINXBITS Win64) @@ -157,15 +202,35 @@ if(NOT PROJECT_ARCH_32BIT AND NOT PROJECT_ARCH_64BIT) endif() endif() -# Set the library directory suffix accordingly -#if(PROJECT_PROC_64BIT) -# # Set the install path to lib64 -# set(PROJECT_LIB_DIR "lib64") -# set(PROJECT_PLUGIN_DIR "lib64/${PROJECT_NAME}-${META_VERSION}") -#else(PROJECT_PROC_64BIT) -# set(PROJECT_LIB_DIR "lib") -# set(PROJECT_PLUGIN_DIR "lib/${PROJECT_NAME}-${META_VERSION}") -#endif() - -#message(STATUS "Installing Libraries to ${CMAKE_INSTALL_PREFIX}/${PROJECT_LIB_DIR}") -#message(STATUS "Installing Plugins to ${CMAKE_INSTALL_PREFIX}/${PROJECT_PLUGIN_DIR}") +# +# Define the library path environment variable name +# + +if(PROJECT_OS_FAMILY STREQUAL "unix") + set(PROJECT_LIBRARY_PATH_NAME "LD_LIBRARY_PATH") +elseif(PROJECT_OS_HAIKU) + set(PROJECT_LIBRARY_PATH_NAME "LIBRARY_PATH") +elseif(PROJECT_OS_WIN OR PROJECT_OS_MINGW) + set(PROJECT_LIBRARY_PATH_NAME "PATH") +elseif(PROJECT_OS_FAMILY STREQUAL "macos") + set(PROJECT_LIBRARY_PATH_NAME "DYLD_LIBRARY_PATH") +else() + message(FATAL_ERROR "Unsupported Platform for PROJECT_LIBRARY_PATH_NAME in Portability") +endif() + +# +# Define a macro for getting the library path with previous contents from environment variable +# + +macro(PROJECT_LIBRARY_PATH variable path) + set(LIBRARY_PATH_ENV_VAR "$ENV{${PROJECT_LIBRARY_PATH_NAME}}") + if("${LIBRARY_PATH_ENV_VAR}" STREQUAL "") + set(${variable} "${path}") + else() + if(PROJECT_OS_WIN OR PROJECT_OS_MINGW) + set(${variable} "${path};${LIBRARY_PATH_ENV_VAR}") + else() + set(${variable} "${path}:${LIBRARY_PATH_ENV_VAR}") + endif() + endif() +endmacro() diff --git a/cmake/ScriptProject.cmake b/cmake/ScriptProject.cmake index db63bbb412..fb7d138355 100644 --- a/cmake/ScriptProject.cmake +++ b/cmake/ScriptProject.cmake @@ -2,7 +2,7 @@ # Script project generator by Parra Studios # Generates a script project embedded into CMake. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,18 +32,18 @@ get_filename_component(SCRIPT_PROJECT_CONFIG_PATH ${CMAKE_CURRENT_LIST_FILE} PAT function(script_project name language configuration) - # Create project file + # Define upper and lower versions of the language string(TOLOWER ${language} language_lower) - # Create project file + # Define project target name set(custom_target "${language_lower}-${name}") + # Define target for the configuration + set(PACKAGE_TARGET "${custom_target}") + # Create project file configure_file(${configuration} ${custom_target}-config.cmake @ONLY) - # Include generated project file - include(${CMAKE_CURRENT_BINARY_DIR}/${custom_target}-config.cmake) - # Set custom target add_custom_target(${custom_target} ALL) @@ -77,4 +77,7 @@ function(script_project name language configuration) ${CMAKE_CURRENT_SOURCE_DIR}/source ${LOADER_SCRIPT_PATH} ) + # Include generated project file + include(${CMAKE_CURRENT_BINARY_DIR}/${custom_target}-config.cmake) + endfunction() diff --git a/cmake/SecurityFlags.cmake b/cmake/SecurityFlags.cmake index 48e418aa27..b31ccbbf7d 100644 --- a/cmake/SecurityFlags.cmake +++ b/cmake/SecurityFlags.cmake @@ -2,7 +2,7 @@ # Compiler and linker options for hardening flags by Parra Studios # Enables hardening security flags if available. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,31 +22,45 @@ include(CheckCCompilerFlagStackSmashing) if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") - # Detect position independent code flag - check_c_compiler_flag("-fPIC" PIC_C_FLAG) + if(OPTION_BUILD_PIC) + # Detect position independent code flag + check_c_compiler_flag("-fPIC" PIC_C_FLAG) - if(PIC_C_FLAG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + if(PIC_C_FLAG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + endif() endif() - # Detect stack protector - check_c_compiler_flag_stack_smashing("-fstack-protector" STACK_PROTECTOR_C_FLAG) + if(OPTION_BUILD_SECURITY) + # Detect stack protector + check_c_compiler_flag_stack_smashing("-fstack-protector-strong" STACK_PROTECTOR_STRONG_C_FLAG) - if(STACK_PROTECTOR_C_FLAG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") + if(STACK_PROTECTOR_STRONG_C_FLAG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong") # use ssp-buffer-size if it is supported if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.9) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --param ssp-buffer-size=4") endif() + else() + check_c_compiler_flag_stack_smashing("-fstack-protector" STACK_PROTECTOR_CXX_FLAG) - endif() + if(STACK_PROTECTOR_C_FLAG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") + + # use ssp-buffer-size if it is supported + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.9) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --param ssp-buffer-size=4") + endif() + endif() + endif() - # Detect fortify source - check_c_compiler_flag("-D_FORTIFY_SOURCE=2" FORTIFY_SOURCE_C_FLAG) + # Detect fortify source + check_c_compiler_flag("-D_FORTIFY_SOURCE=2" FORTIFY_SOURCE_C_FLAG) - if(FORTIFY_SOURCE_C_FLAG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O1 -D_FORTIFY_SOURCE=2") + if(FORTIFY_SOURCE_C_FLAG) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -D_FORTIFY_SOURCE=2") + endif() endif() endif() @@ -56,32 +70,45 @@ include(CheckCXXCompilerFlagStackSmashing) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # Detect position independent code flag - check_cxx_compiler_flag("-fPIC" PIC_CXX_FLAG) + if(OPTION_BUILD_PIC) + # Detect position independent code flag + check_cxx_compiler_flag("-fPIC" PIC_CXX_FLAG) - if(PIC_CXX_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + if(PIC_CXX_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + endif() endif() - # Detect stack protector - check_cxx_compiler_flag_stack_smashing("-fstack-protector" STACK_PROTECTOR_CXX_FLAG) + if(OPTION_BUILD_SECURITY) + # Detect stack protector + check_cxx_compiler_flag_stack_smashing("-fstack-protector-strong" STACK_PROTECTOR_STRONG_CXX_FLAG) - if(STACK_PROTECTOR_CXX_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector") + if(STACK_PROTECTOR_STRONG_CXX_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong") # use ssp-buffer-size if it is supported if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --param ssp-buffer-size=4") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --param ssp-buffer-size=4") endif() + else() + check_cxx_compiler_flag_stack_smashing("-fstack-protector" STACK_PROTECTOR_CXX_FLAG) - endif() + if(STACK_PROTECTOR_CXX_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector") + + # use ssp-buffer-size if it is supported + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --param ssp-buffer-size=4") + endif() + endif() + endif() - # Detect fortify source - check_cxx_compiler_flag("-D_FORTIFY_SOURCE=2" FORTIFY_SOURCE_CXX_FLAG) + # Detect fortify source + check_cxx_compiler_flag("-D_FORTIFY_SOURCE=2" FORTIFY_SOURCE_CXX_FLAG) - if(FORTIFY_SOURCE_CXX_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1 -D_FORTIFY_SOURCE=2") + if(FORTIFY_SOURCE_CXX_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -D_FORTIFY_SOURCE=2") + endif() endif() endif() - diff --git a/cmake/TestEnvironmentVariables.cmake b/cmake/TestEnvironmentVariables.cmake index 7343b9fee0..1255f2bb46 100644 --- a/cmake/TestEnvironmentVariables.cmake +++ b/cmake/TestEnvironmentVariables.cmake @@ -2,7 +2,7 @@ # Test Environment Variables by Parra Studios # Utility for defining cross-platform environment variables in tests. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/Warnings.cmake b/cmake/Warnings.cmake index 282989f660..23f057afd2 100644 --- a/cmake/Warnings.cmake +++ b/cmake/Warnings.cmake @@ -2,7 +2,7 @@ # Cross-compiler warning utility by Parra Studios # Utility to enable cross-compiler warnings. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ if(WARNINGS_ENABLED) # Define C compiler warning flags if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + # TODO: Uncomment the rest of the warnings, enable Weverything for clang add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-Wunused) @@ -82,12 +83,15 @@ if(WARNINGS_ENABLED) set(WARNINGS_C_AVAILABLE 1) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4 /Wall /WX") + string(REPLACE "/W1" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "/W2" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "/W3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") # /Wall set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CTR_NONSTDC_NO_WARNINGS=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CTR_SECURE_NO_WARNINGS=1") set(WARNINGS_C_AVAILABLE 1) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W5 /Wall /Wcheck /Werror-all /WX") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W5 /Wall /Wcheck /Werror-all") set(WARNINGS_C_AVAILABLE 1) else() set(STATUS "Unknown C compiler warning level support") @@ -99,14 +103,17 @@ if(WARNINGS_ENABLED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(WARNINGS_CXX_AVAILABLE 1) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /Wall /WX") + string(REPLACE "/W1" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/W2" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") # /Wall set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_NONSTDC_NO_WARNINGS=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _CTR_SECURE_NO_WARNINGS=1") set(WARNINGS_CXX_AVAILABLE 1) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W5 /Wall /Wcheck /Werror-all /WX") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W5 /Wall /Wcheck /Werror-all") set(WARNINGS_CXX_AVAILABLE 1) else() set(STATUS "Unknown C++ compiler warning level support") diff --git a/cmake/coverage/FindGcov.cmake b/cmake/coverage/FindGcov.cmake deleted file mode 100644 index 157fc88c6d..0000000000 --- a/cmake/coverage/FindGcov.cmake +++ /dev/null @@ -1,149 +0,0 @@ -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - - -# include required Modules -include(FindPackageHandleStandardArgs) - -# Search for gcov binary. -set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) -set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) - -get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach (LANG ${ENABLED_LANGUAGES}) - # Gcov evaluation is dependent on the used compiler. Check gcov support for - # each compiler that is used. If gcov binary was already found for this - # compiler, do not try to find it again. - if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN) - get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH) - - if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU") - # Some distributions like OSX (homebrew) ship gcov with the compiler - # version appended as gcov-x. To find this binary we'll build the - # suggested binary name with the compiler version. - string(REGEX MATCH "^[0-9]+" GCC_VERSION - "${CMAKE_${LANG}_COMPILER_VERSION}") - - find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov - HINTS ${COMPILER_PATH}) - - elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang") - # Some distributions like Debian ship llvm-cov with the compiler - # version appended as llvm-cov-x.y. To find this binary we'll build - # the suggested binary name with the compiler version. - string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION - "${CMAKE_${LANG}_COMPILER_VERSION}") - - # llvm-cov prior version 3.5 seems to be not working with coverage - # evaluation tools, but these versions are compatible with the gcc - # gcov tool. - if(LLVM_VERSION VERSION_GREATER 3.4) - find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}" - "llvm-cov" HINTS ${COMPILER_PATH}) - mark_as_advanced(LLVM_COV_BIN) - - if (LLVM_COV_BIN) - find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS - ${CMAKE_MODULE_PATH}) - if (LLVM_COV_WRAPPER) - set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "") - - # set additional parameters - set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV - "LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING - "Environment variables for llvm-cov-wrapper.") - mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV) - endif () - endif () - endif () - - if (NOT GCOV_BIN) - # Fall back to gcov binary if llvm-cov was not found or is - # incompatible. This is the default on OSX, but may crash on - # recent Linux versions. - find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH}) - endif () - endif () - - - if (GCOV_BIN) - set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING - "${LANG} gcov binary.") - - if (NOT CMAKE_REQUIRED_QUIET) - message("-- Found gcov evaluation for " - "${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}") - endif() - - unset(GCOV_BIN CACHE) - endif () - endif () -endforeach () - -# Add a new global target for all gcov targets. This target could be used to -# generate the gcov files for the whole project instead of calling -gcov -# for each target. -if (NOT TARGET gcov) - add_custom_target(gcov) -endif (NOT TARGET gcov) - -# This function will add gcov evaluation for target . Only sources of -# this target will be evaluated and no dependencies will be added. It will call -# Gcov on any source file of once and store the gcov file in the same -# directory. -function (add_gcov_target TNAME) - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) - - # We don't have to check, if the target has support for coverage, thus this - # will be checked by add_coverage_target in Findcoverage.cmake. Instead we - # have to determine which gcov binary to use. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(SOURCES "") - set(TCOMPILER "") - foreach (FILE ${TSOURCES}) - codecov_path_of_source(${FILE} FILE) - if (NOT "${FILE}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (NOT "${LANG}" STREQUAL "") - list(APPEND SOURCES "${FILE}") - set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - # If no gcov binary was found, coverage data can't be evaluated. - if (NOT GCOV_${TCOMPILER}_BIN) - message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") - return() - endif () - - set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") - set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - - set(BUFFER "") - foreach(FILE ${SOURCES}) - get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH) - - # call gcov - add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov - COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null - DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno - WORKING_DIRECTORY ${FILE_PATH} - ) - - list(APPEND BUFFER ${TDIR}/${FILE}.gcov) - endforeach() - - # add target for gcov evaluation of - add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER}) - - # add evaluation target to the global gcov target. - add_dependencies(gcov ${TNAME}-gcov) -endfunction (add_gcov_target) diff --git a/cmake/coverage/FindLcov.cmake b/cmake/coverage/FindLcov.cmake deleted file mode 100644 index beb925ae06..0000000000 --- a/cmake/coverage/FindLcov.cmake +++ /dev/null @@ -1,354 +0,0 @@ -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - - -# configuration -set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data") -set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init") -set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture") -set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html") - - - - -# Search for Gcov which is used by Lcov. -find_package(Gcov) - - - - -# This function will add lcov evaluation for target . Only sources of -# this target will be evaluated and no dependencies will be added. It will call -# geninfo on any source file of once and store the info file in the same -# directory. -# -# Note: This function is only a wrapper to define this function always, even if -# coverage is not supported by the compiler or disabled. This function must -# be defined here, because the module will be exited, if there is no coverage -# support by the compiler or it is disabled by the user. -function (add_lcov_target TNAME) - if (LCOV_FOUND) - # capture initial coverage data - lcov_capture_initial_tgt(${TNAME}) - - # capture coverage data after execution - lcov_capture_tgt(${TNAME}) - endif () -endfunction (add_lcov_target) - - - - -# include required Modules -include(FindPackageHandleStandardArgs) - -# Search for required lcov binaries. -find_program(LCOV_BIN lcov) -find_program(GENINFO_BIN geninfo) -find_program(GENHTML_BIN genhtml) -find_package_handle_standard_args(lcov - REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN -) - -# enable genhtml C++ demangeling, if c++filt is found. -set(GENHTML_CPPFILT_FLAG "") -find_program(CPPFILT_BIN c++filt) -if (NOT CPPFILT_BIN STREQUAL "") - set(GENHTML_CPPFILT_FLAG "--demangle-cpp") -endif (NOT CPPFILT_BIN STREQUAL "") - -# enable no-external flag for lcov, if available. -if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG) - set(FLAG "") - execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP) - string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}") - if (GENINFO_RES) - set(FLAG "--no-external") - endif () - - set(GENINFO_EXTERN_FLAG "${FLAG}" - CACHE STRING "Geninfo flag to exclude system sources.") -endif () - -# If Lcov was not found, exit module now. -if (NOT LCOV_FOUND) - return() -endif (NOT LCOV_FOUND) - - - - -# Create directories to be used. -file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT}) -file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE}) - -set(LCOV_REMOVE_PATTERNS "") - -# This function will merge lcov files to a single target file. Additional lcov -# flags may be set with setting LCOV_EXTRA_FLAGS before calling this function. -function (lcov_merge_files OUTFILE ...) - # Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files. - list(REMOVE_AT ARGV 0) - - # Generate merged file. - string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}") - add_custom_command(OUTPUT "${OUTFILE}.raw" - COMMAND cat ${ARGV} > ${OUTFILE}.raw - DEPENDS ${ARGV} - COMMENT "Generating ${FILE_REL}" - ) - - add_custom_command(OUTPUT "${OUTFILE}" - COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE} - --base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS} - COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS} - --output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS} - DEPENDS ${OUTFILE}.raw - COMMENT "Post-processing ${FILE_REL}" - ) -endfunction () - - - - -# Add a new global target to generate initial coverage reports for all targets. -# This target will be used to generate the global initial info file, which is -# used to gather even empty report data. -if (NOT TARGET lcov-capture-init) - add_custom_target(lcov-capture-init) - set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "") -endif (NOT TARGET lcov-capture-init) - - -# This function will add initial capture of coverage data for target , -# which is needed to get also data for objects, which were not loaded at -# execution time. It will call geninfo for every source file of once and -# store the info file in the same directory. -function (lcov_capture_initial_tgt TNAME) - # We don't have to check, if the target has support for coverage, thus this - # will be checked by add_coverage_target in Findcoverage.cmake. Instead we - # have to determine which gcov binary to use. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(SOURCES "") - set(TCOMPILER "") - foreach (FILE ${TSOURCES}) - codecov_path_of_source(${FILE} FILE) - if (NOT "${FILE}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (NOT "${LANG}" STREQUAL "") - list(APPEND SOURCES "${FILE}") - set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - # If no gcov binary was found, coverage data can't be evaluated. - if (NOT GCOV_${TCOMPILER}_BIN) - message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") - return() - endif () - - set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") - set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - - - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) - set(GENINFO_FILES "") - foreach(FILE ${SOURCES}) - # generate empty coverage files - set(OUTFILE "${TDIR}/${FILE}.info.init") - list(APPEND GENINFO_FILES ${OUTFILE}) - - add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN} - --quiet --base-directory ${PROJECT_SOURCE_DIR} --initial - --gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE} - ${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno - DEPENDS ${TNAME} - COMMENT "Capturing initial coverage data for ${FILE}" - ) - endforeach() - - # Concatenate all files generated by geninfo to a single file per target. - set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info") - set(LCOV_EXTRA_FLAGS "--initial") - lcov_merge_files("${OUTFILE}" ${GENINFO_FILES}) - add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE}) - - # add geninfo file generation to global lcov-geninfo target - add_dependencies(lcov-capture-init ${TNAME}-capture-init) - set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}" - "${OUTFILE}" CACHE INTERNAL "" - ) -endfunction (lcov_capture_initial_tgt) - - -# This function will generate the global info file for all targets. It has to be -# called after all other CMake functions in the root CMakeLists.txt file, to get -# a full list of all targets that generate coverage data. -function (lcov_capture_initial) - # Skip this function (and do not create the following targets), if there are - # no input files. - if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "") - return() - endif () - - # Add a new target to merge the files of all targets. - set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info") - lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES}) - add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE} - lcov-capture-init - ) -endfunction (lcov_capture_initial) - - - - -# Add a new global target to generate coverage reports for all targets. This -# target will be used to generate the global info file. -if (NOT TARGET lcov-capture) - add_custom_target(lcov-capture) - set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "") -endif (NOT TARGET lcov-capture) - - -# This function will add capture of coverage data for target , which is -# needed to get also data for objects, which were not loaded at execution time. -# It will call geninfo for every source file of once and store the info -# file in the same directory. -function (lcov_capture_tgt TNAME) - # We don't have to check, if the target has support for coverage, thus this - # will be checked by add_coverage_target in Findcoverage.cmake. Instead we - # have to determine which gcov binary to use. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(SOURCES "") - set(TCOMPILER "") - foreach (FILE ${TSOURCES}) - codecov_path_of_source(${FILE} FILE) - if (NOT "${FILE}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (NOT "${LANG}" STREQUAL "") - list(APPEND SOURCES "${FILE}") - set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - # If no gcov binary was found, coverage data can't be evaluated. - if (NOT GCOV_${TCOMPILER}_BIN) - message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.") - return() - endif () - - set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") - set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - - - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) - set(GENINFO_FILES "") - foreach(FILE ${SOURCES}) - # Generate coverage files. If no .gcda file was generated during - # execution, the empty coverage file will be used instead. - set(OUTFILE "${TDIR}/${FILE}.info") - list(APPEND GENINFO_FILES ${OUTFILE}) - - add_custom_command(OUTPUT ${OUTFILE} - COMMAND test -f "${TDIR}/${FILE}.gcda" - && ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory - ${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN} - --output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG} - ${TDIR}/${FILE}.gcda - || cp ${OUTFILE}.init ${OUTFILE} - DEPENDS ${TNAME} ${TNAME}-capture-init - COMMENT "Capturing coverage data for ${FILE}" - ) - endforeach() - - # Concatenate all files generated by geninfo to a single file per target. - set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info") - lcov_merge_files("${OUTFILE}" ${GENINFO_FILES}) - add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE}) - - # add geninfo file generation to global lcov-capture target - add_dependencies(lcov-capture ${TNAME}-geninfo) - set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL - "" - ) - - # Add target for generating html output for this target only. - file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME}) - add_custom_target(${TNAME}-genhtml - COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR} - --baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info - --output-directory ${LCOV_HTML_PATH}/${TNAME} - --title "${CMAKE_PROJECT_NAME} - target ${TNAME}" - ${GENHTML_CPPFILT_FLAG} ${OUTFILE} - DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init - ) -endfunction (lcov_capture_tgt) - - -# This function will generate the global info file for all targets. It has to be -# called after all other CMake functions in the root CMakeLists.txt file, to get -# a full list of all targets that generate coverage data. -function (lcov_capture) - # Skip this function (and do not create the following targets), if there are - # no input files. - if ("${LCOV_CAPTURE_FILES}" STREQUAL "") - return() - endif () - - # Add a new target to merge the files of all targets. - set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info") - lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES}) - add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture) - - # Add a new global target for all lcov targets. This target could be used to - # generate the lcov html output for the whole project instead of calling - # -geninfo and -genhtml for each target. It will also be - # used to generate a html site for all project data together instead of one - # for each target. - if (NOT TARGET lcov) - file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets) - add_custom_target(lcov - COMMAND ${GENHTML_BIN} --quiet --sort - --baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info - --output-directory ${LCOV_HTML_PATH}/all_targets - --title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}" - ${GENHTML_CPPFILT_FLAG} ${OUTFILE} - DEPENDS lcov-geninfo-init lcov-geninfo - ) - endif () -endfunction (lcov_capture) - - - - -# Add a new global target to generate the lcov html report for the whole project -# instead of calling -genhtml for each target (to create an own report -# for each target). Instead of the lcov target it does not require geninfo for -# all targets, so you have to call -geninfo to generate the info files -# the targets you'd like to have in your report or lcov-geninfo for generating -# info files for all targets before calling lcov-genhtml. -file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets) -if (NOT TARGET lcov-genhtml) - add_custom_target(lcov-genhtml - COMMAND ${GENHTML_BIN} - --quiet - --output-directory ${LCOV_HTML_PATH}/selected_targets - --title \"${CMAKE_PROJECT_NAME} - targets `find - ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name - \"all_targets.info\" -exec basename {} .info \\\;`\" - --prefix ${PROJECT_SOURCE_DIR} - --sort - ${GENHTML_CPPFILT_FLAG} - `find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name - \"all_targets.info\"` - ) -endif (NOT TARGET lcov-genhtml) diff --git a/cmake/coverage/Findcodecov.cmake b/cmake/coverage/Findcodecov.cmake deleted file mode 100644 index 5bacd950c1..0000000000 --- a/cmake/coverage/Findcodecov.cmake +++ /dev/null @@ -1,312 +0,0 @@ -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - -# Add an option to choose, if coverage should be enabled or not. If enabled -# marked targets will be build with coverage support and appropriate targets -# will be added. If disabled coverage will be ignored for *ALL* targets. -option(ENABLE_COVERAGE "Enable coverage build." OFF) - -# Add an option to choose, if coverage should be enabled for all targets, even -# those which are not explictly marked as coverage targets. If disabled, only -# targets added by add_coverage will be marked for coverage build. This option -# is only available, if coverage was enabled. -if (ENABLE_COVERAGE) - option(ENABLE_COVERAGE_ALL "Enable coverage build for all targets." OFF) -endif () - -set(COVERAGE_FLAG_CANDIDATES - # gcc and clang - "-O0 -g -fprofile-arcs -ftest-coverage" - - # gcc and clang fallback - "-O0 -g --coverage" -) - -# Add coverage support for target ${TNAME} and register target for coverage -# evaluation. If coverage is disabled or not supported, this function will -# simply do nothing. -# -# Note: This function is only a wrapper to define this function always, even if -# coverage is not supported by the compiler or disabled. This function must -# be defined here, because the module will be exited, if there is no coverage -# support by the compiler or it is disabled by the user. -function (add_coverage TNAME) - # only add coverage for target, if coverage is support and enabled. - if (ENABLE_COVERAGE) - foreach (TNAME ${ARGV}) - add_coverage_target(${TNAME}) - endforeach () - endif () -endfunction (add_coverage) - -# Add global target to gather coverage information after all targets have been -# added. Other evaluation functions could be added here, after checks for the -# specific module have been passed. -# -# Note: This function is only a wrapper to define this function always, even if -# coverage is not supported by the compiler or disabled. This function must -# be defined here, because the module will be exited, if there is no coverage -# support by the compiler or it is disabled by the user. -function (coverage_evaluate) - # add lcov evaluation - if (LCOV_FOUND) - lcov_capture_initial() - lcov_capture() - endif (LCOV_FOUND) -endfunction () - -# Exit this module, if coverage is disabled. add_coverage is defined before this -# return, so this module can be exited now safely without breaking any build- -# scripts. -if (NOT ENABLE_COVERAGE) - return() -endif () - -# Find the required flags foreach language. -set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) -set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) - -get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach (LANG ${ENABLED_LANGUAGES}) - # Coverage flags are not dependent on language, but the used compiler. So - # instead of searching flags foreach language, search flags foreach compiler - # used. - set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT COVERAGE_${COMPILER}_FLAGS) - foreach (FLAG ${COVERAGE_FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(COVERAGE_FLAG_DETECTED CACHE) - - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To be - # compatible with older Cmake versions, we will check if this - # module is present before we use it. Otherwise we will define - # Fortran coverage support as not available. - include(CheckFortranCompilerFlag OPTIONAL - RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" - COVERAGE_FLAG_DETECTED) - elseif (NOT CMAKE_REQUIRED_QUIET) - message("-- Performing Test COVERAGE_FLAG_DETECTED") - message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed" - " (Check not supported)") - endif () - endif() - - if (COVERAGE_FLAG_DETECTED) - set(COVERAGE_${COMPILER}_FLAGS "${FLAG}" - CACHE STRING "${COMPILER} flags for code coverage.") - mark_as_advanced(COVERAGE_${COMPILER}_FLAGS) - break() - else () - message(WARNING "Code coverage is not available for ${COMPILER}" - " compiler. Targets using this compiler will be " - "compiled without it.") - endif () - endforeach () - endif () -endforeach () - -set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) - -# Helper function to get the language of a source file. -function (codecov_lang_of_source FILE RETURN_VAR) - get_filename_component(FILE_EXT "${FILE}" EXT) - string(TOLOWER "${FILE_EXT}" FILE_EXT) - string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) - - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) - if (NOT ${TEMP} EQUAL -1) - set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) - return() - endif () - endforeach() - - set(${RETURN_VAR} "" PARENT_SCOPE) -endfunction () - -# Helper function to get the relative path of the source file destination path. -# This path is needed by FindGcov and FindLcov cmake files to locate the -# captured data. -function (codecov_path_of_source FILE RETURN_VAR) - string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE}) - - # If expression was found, SOURCEFILE is a generator-expression for an - # object library. Currently we found no way to call this function automatic - # for the referenced target, so it must be called in the directoryso of the - # object library definition. - if (NOT "${_source}" STREQUAL "") - set(${RETURN_VAR} "" PARENT_SCOPE) - return() - endif () - - string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}") - if(IS_ABSOLUTE ${FILE}) - file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE}) - endif() - - # get the right path for file - string(REPLACE ".." "__" PATH "${FILE}") - - set(${RETURN_VAR} "${PATH}" PARENT_SCOPE) -endfunction() - -# Add coverage support for target ${TNAME} and register target for coverage -# evaluation. -function(add_coverage_target TNAME) - # Check if all sources for target use the same compiler. If a target uses - # e.g. C and Fortran mixed and uses different compilers (e.g. clang and - # gfortran) this can trigger huge problems, because different compilers may - # use different implementations for code coverage. - get_target_property(TSOURCES ${TNAME} SOURCES) - set(TARGET_COMPILER "") - set(ADDITIONAL_FILES "") - foreach (FILE ${TSOURCES}) - # If expression was found, FILE is a generator-expression for an object - # library. Object libraries will be ignored. - string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) - if ("${_file}" STREQUAL "") - codecov_lang_of_source(${FILE} LANG) - if (LANG) - list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - - list(APPEND ADDITIONAL_FILES "${FILE}.gcno") - list(APPEND ADDITIONAL_FILES "${FILE}.gcda") - endif () - endif () - endforeach () - - list(REMOVE_DUPLICATES TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - - if (NUM_COMPILERS GREATER 1) - message(WARNING "Can't use code coverage for target ${TNAME}, because " - "it will be compiled by incompatible compilers. Target will be " - "compiled without code coverage.") - return() - elseif (NUM_COMPILERS EQUAL 0) - message(WARNING "Can't use code coverage for target ${TNAME}, because " - "it uses an unknown compiler. Target will be compiled without " - "code coverage.") - return() - elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS") - # A warning has been printed before, so just return if flags for this - # compiler aren't available. - return() - endif() - - # enable coverage for target - set_property(TARGET ${TNAME} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TNAME} APPEND_STRING - PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}") - - # Add gcov files generated by compiler to clean target. - set(CLEAN_FILES "") - foreach (FILE ${ADDITIONAL_FILES}) - codecov_path_of_source(${FILE} FILE) - list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}") - endforeach() - - set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES - "${CLEAN_FILES}") - - - add_gcov_target(${TNAME}) - add_lcov_target(${TNAME}) -endfunction(add_coverage_target) - -# If ENABLE_COVERAGE_ALL is enabled, overload add_executable and add_library -# functions, to add coverage support for *ALL* targets. The functions will call -# the overloaded functions first and then add_coverage. -if (ENABLE_COVERAGE_ALL) - function(add_executable ARGV) - # add executable - _add_executable(${ARGV}) - - get_target_property(TARGET_TYPE "${ARGV0}" TYPE) - - if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") - return() - endif() - - # check if target is supported for code coverage - get_target_property(TSOURCES ${ARGV0} SOURCES) - foreach (FILE ${TSOURCES}) - get_source_file_property(SLANG ${FILE} LANGUAGE) - if ((NOT ${SLANG} STREQUAL "C") AND (NOT ${SLANG} STREQUAL "CXX")) - # Target has source files that are not supported for code - # coverage. Do not add coverage for this target and print a - # warning. - message("-- Code coverage not supported for target ${ARGV0}") - return() - endif() - endforeach () - - get_target_property(ALIASED ${ARGV0} ALIASED_TARGET) - - if(NOT ALIASED) - # add coverage (if not alias and not interface) - add_coverage(${ARGV0}) - endif() - endfunction(add_executable) - - function(add_library ARGV) - # add library - _add_library(${ARGV}) - - get_target_property(TARGET_TYPE "${ARGV0}" TYPE) - - if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") - return() - endif() - - # check if target is supported for code coverage - get_target_property(TSOURCES ${ARGV0} SOURCES) - foreach (FILE ${TSOURCES}) - get_source_file_property(SLANG ${FILE} LANGUAGE) - if ((NOT ${SLANG} STREQUAL "C") AND (NOT ${SLANG} STREQUAL "CXX")) - # Target has source files that are not supported for code - # coverage. Do not add coverage for this target and print a - # warning. - message("-- Code coverage not supported for target ${ARGV0}") - return() - endif() - endforeach () - - get_target_property(ALIASED ${ARGV0} ALIASED_TARGET) - - if(NOT ALIASED) - # add coverage (if not alias) - add_coverage(${ARGV0}) - endif() - - endfunction(add_library) -endif () - -# Include modules for parsing the collected data and output it in a readable -# format (like gcov and lcov). -find_package(Gcov) -find_package(Lcov) diff --git a/cmake/coverage/LICENSE b/cmake/coverage/LICENSE deleted file mode 100644 index 7f786a8415..0000000000 --- a/cmake/coverage/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright 2015-2017 RWTH Aachen University, Federal Republic of Germany -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/coverage/llvm-cov-wrapper b/cmake/coverage/llvm-cov-wrapper deleted file mode 100644 index a804898945..0000000000 --- a/cmake/coverage/llvm-cov-wrapper +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env sh - -# This file is part of CMake-codecov. -# -# Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany -# -# See the LICENSE file in the package base directory for details -# -# Written by Alexander Haase, alexander.haase@rwth-aachen.de -# - -if [ -z "$LLVM_COV_BIN" ] -then - echo "LLVM_COV_BIN not set!" >& 2 - exit 1 -fi - - -# Get LLVM version to find out. -LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \ - | sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g") - -if [ "$1" = "-v" ] -then - echo "llvm-cov-wrapper $LLVM_VERSION" - exit 0 -fi - - -if [ -n "$LLVM_VERSION" ] -then - MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1) - MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2) - - if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ] - then - if [ -f "$1" ] - then - filename=$(basename "$1") - extension="${filename##*.}" - - case "$extension" in - "gcno") exec $LLVM_COV_BIN --gcno="$1" ;; - "gcda") exec $LLVM_COV_BIN --gcda="$1" ;; - esac - fi - fi - - if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ] - then - exec $LLVM_COV_BIN $@ - fi -fi - -exec $LLVM_COV_BIN gcov $@ diff --git a/deploy/packages/descriptor-metacall.json b/deploy/packages/descriptor-metacall.json index aafc437270..79c7fe2bab 100644 --- a/deploy/packages/descriptor-metacall.json +++ b/deploy/packages/descriptor-metacall.json @@ -26,7 +26,7 @@ "files": [{ "includePattern": "artifacts/packages/(.*\\.deb)", "uploadPattern": "$1", "matrixParams": { - "deb_distribution": "stretch", + "deb_distribution": "bookworm", "deb_component": "main", "deb_architecture": "amd64" } diff --git a/deploy/packages/descriptor-metacall.json.in b/deploy/packages/descriptor-metacall.json.in index 5d7bb06044..1b12ab7aed 100644 --- a/deploy/packages/descriptor-metacall.json.in +++ b/deploy/packages/descriptor-metacall.json.in @@ -27,7 +27,7 @@ "files": [{ "includePattern": "@DEPLOY_ARTIFACTS_PATH@/(.*\\.deb)", "uploadPattern": "$1", "matrixParams": { - "deb_distribution": "stretch", + "deb_distribution": "bookworm", "deb_component": "main", "deb_architecture": "amd64" } diff --git a/deploy/packages/pack-metacall.cmake b/deploy/packages/pack-metacall.cmake index 0cbcf4b8cf..d271946f49 100644 --- a/deploy/packages/pack-metacall.cmake +++ b/deploy/packages/pack-metacall.cmake @@ -184,7 +184,7 @@ endif() set(CPACK_DEBIAN_PACKAGE_NAME "${package_name}") set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all") -set(CPACK_DEBIAN_RUNTIME_PACKAGE_DEPENDS "bash (>= 4.4-5), curl (>= 7.52.1-5), libc6 (>= 2.24-11), libgcc1 (>= 1:6.3.0-18), libpython3.5 (>= 3.5.3-1), libruby (>= 1:2.3.3), libicu57 (>= 57.1-6), libunwind8 (>= 1.1-4.1)") +set(CPACK_DEBIAN_RUNTIME_PACKAGE_DEPENDS "bash (>= 4.4-5), curl (>= 7.52.1-5), libc6 (>= 2.24-11), libgcc1 (>= 1:6.3.0-18), libpython3.7 (>= 3.7.3-2), libruby (>= 1:2.5.5), libicu57 (>= 57.1-6), libunwind8 (>= 1.1-4.1)") set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${package_maintainer}") set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") set(CPACK_DEBIAN_PACKAGE_SECTION "devel") @@ -203,7 +203,7 @@ set(CPACK_RPM_PACKAGE_NAME "${package_name}") set(CPACK_RPM_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") set(CPACK_RPM_PACKAGE_RELEASE 1) set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") -set(CPACK_RPM_PACKAGE_REQUIRES "bash >= 4.4, curl >= 7.52.1, libc6 >= 2.24, libgcc1 >= 6.3.0, libpython3.5 >= 3.5.3, libruby >= 2.3.3, libicu57 >= 57.1, libunwind8 >= 1.1-4.1") +set(CPACK_RPM_PACKAGE_REQUIRES "bash >= 4.4, curl >= 7.52.1, libc6 >= 2.24, libgcc1 >= 6.3.0, libpython3.7 >= 3.7.3, libruby >= 2.5.5, libicu57 >= 57.1, libunwind8 >= 1.1-4.1") set(CPACK_RPM_PACKAGE_PROVIDES "") set(CPACK_RPM_PACKAGE_VENDOR "${package_vendor}") set(CPACK_RPM_PACKAGE_LICENSE "Apache License Version 2.0") diff --git a/deploy/packages/postinst b/deploy/packages/postinst index bd03cc4359..8ae7a6fa6c 100755 --- a/deploy/packages/postinst +++ b/deploy/packages/postinst @@ -4,7 +4,7 @@ # MetaCall Dependencies Bash Script by Parra Studios # Remove all packages and unused data from MetaCall building and testing. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ NETCORE_INSTALL_DIR=/usr/share/dotnet +# TODO: NetCore 2 + # Download and install NetCore if [ ! -f $NETCORE_INSTALL_DIR/dotnet ]; then curl -s https://dot.net/v1/dotnet-install.sh &> dotnet-install.sh \ diff --git a/docker-compose.cache.yml b/docker-compose.cache.yml index d58c23316e..63b1b455c7 100644 --- a/docker-compose.cache.yml +++ b/docker-compose.cache.yml @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker compose infrastructure for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,17 +20,10 @@ version: "3.7" services: - deps_node: - image: metacall/core:deps_node - build: - cache_from: - - ${IMAGE_REGISTRY}/metacall/core:deps_node - deps: image: metacall/core:deps build: cache_from: - - ${IMAGE_REGISTRY}/metacall/core:deps_node - ${IMAGE_REGISTRY}/metacall/core:deps dev: @@ -44,3 +37,9 @@ services: build: cache_from: - ${IMAGE_REGISTRY}/metacall/core:runtime + + cli: + image: metacall/core:cli + build: + cache_from: + - ${IMAGE_REGISTRY}/metacall/core:cli diff --git a/docker-compose.sh b/docker-compose.sh index ee75422c74..e254cb103e 100755 --- a/docker-compose.sh +++ b/docker-compose.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash # # MetaCall Build Bash Script by Parra Studios # Build and install bash script utility for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,24 @@ # limitations under the License. # +set -euxo pipefail + +# Enable BuildKit whenever possible +export COMPOSE_DOCKER_CLI_BUILD=1 +export DOCKER_BUILDKIT=1 +export BUILDKIT_PROGRESS=plain +export PROGRESS_NO_TRUNC=1 + +# Check if docker compose command is available +if [ -x "$(command -v docker-compose)" ]; then + DOCKER_COMPOSE="docker-compose" +elif $(docker compose &>/dev/null) && [ $? -eq 0 ]; then + DOCKER_COMPOSE="docker compose" +else + echo "Docker Compose not installed, install it and re-run the script" + exit 1 +fi + # Pull MetaCall Docker Compose sub_pull() { if [ -z "$IMAGE_NAME" ]; then @@ -26,51 +44,139 @@ sub_pull() { exit 1 fi - docker pull $IMAGE_NAME:deps_node || true + docker pull $IMAGE_NAME:deps && docker tag $IMAGE_NAME:deps metacall/core:deps || true - docker pull $IMAGE_NAME:deps || true + docker pull $IMAGE_NAME:dev && docker tag $IMAGE_NAME:dev metacall/core:dev || true - docker pull $IMAGE_NAME:dev || true + docker pull $IMAGE_NAME:runtime && docker tag $IMAGE_NAME:runtime metacall/core:runtime || true - docker pull $IMAGE_NAME:runtime || true - - docker pull $IMAGE_NAME:cli || true + docker pull $IMAGE_NAME:cli && docker tag $IMAGE_NAME:cli metacall/core:cli || true } # Build MetaCall Docker Compose (link manually dockerignore files) sub_build() { - ln -sf tools/node/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm deps_node - - ln -sf tools/base/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm deps + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm deps ln -sf tools/dev/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm dev + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm dev - ln -sf tools/core/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm runtime + ln -sf tools/runtime/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm runtime ln -sf tools/cli/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm cli + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm cli } # Build MetaCall Docker Compose without cache (link manually dockerignore files) sub_rebuild() { - ln -sf tools/node/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache deps_node - - ln -sf tools/base/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache deps + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache deps ln -sf tools/dev/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache dev + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache dev - ln -sf tools/core/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache runtime + ln -sf tools/runtime/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache runtime ln -sf tools/cli/.dockerignore .dockerignore - docker-compose -f docker-compose.yml build --force-rm --no-cache cli + $DOCKER_COMPOSE -f docker-compose.yml build --force-rm --no-cache cli +} + +# Build MetaCall Docker Compose for testing (link manually dockerignore files) +sub_test() { + # Disable BuildKit as workaround due to log limits (TODO: https://github.com/docker/buildx/issues/484) + export DOCKER_BUILDKIT=0 + + # Disable build with sanitizer + export METACALL_BUILD_SANITIZER= + + # Disable build with coverage + export METACALL_BUILD_COVERAGE= + + # Define build type + export METACALL_BUILD_TYPE=${METACALL_BUILD_TYPE:-debug} + + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm deps + + ln -sf tools/dev/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev +} + +# Build MetaCall Docker Compose with Sanitizer for testing (link manually dockerignore files) +sub_test_sanitizer() { + # Disable BuildKit as workaround due to log limits (TODO: https://github.com/docker/buildx/issues/484) + export DOCKER_BUILDKIT=0 + + # Enable build with sanitizer + export METACALL_BUILD_SANITIZER=${METACALL_BUILD_SANITIZER:-address-sanitizer} + + # Disable build with coverage + export METACALL_BUILD_COVERAGE= + + # Define build type + export METACALL_BUILD_TYPE=${METACALL_BUILD_TYPE:-debug} + + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm deps + + ln -sf tools/dev/.dockerignore .dockerignore + + if [ ! -z "${SANITIZER_SKIP_SUMMARY:-}" ]; then + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev + else + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev | tee /tmp/metacall-test-output + + # Retrieve all the summaries + SUMMARY=$(grep "SUMMARY:" /tmp/metacall-test-output) + echo "${SUMMARY}" + printf "Number of leaks detected: " + echo "${SUMMARY}" | awk '{print $7}' | awk '{s+=$1} END {print s}' + + # Count the number of tests that really failed and avoid the false positives + FAILED=$(grep "FAILED TEST" /tmp/metacall-test-output) + printf "Number of tests failed: " + echo "${FAILED}" | awk '{print $1}' | awk '{s+=$1} END {print s}' + + # Get the potential tests that failed + BEGIN=$(grep -n "The following tests FAILED:" /tmp/metacall-test-output | cut -d : -f 1) + END=$(grep -n "Errors while running CTest" /tmp/metacall-test-output | cut -d : -f 1) + + if [ -z "${BEGIN}" ] || [ -z "${END}" ]; then + echo "ERROR! CTest failed to print properly the output, run tests again:" + echo " Recompiling everything: docker rmi metacall/core:dev && ./docker-compose.sh test-${METACALL_BUILD_SANITIZER}" + echo " Without recompiling (needs to be built successfully previously): docker run --rm -it metacall/core:dev sh -c \"cd build && ctest -j$(getconf _NPROCESSORS_ONLN) --output-on-failure\"" + else + BEGIN=$((BEGIN + 1)) + END=$((END - 1)) + echo "List of potential failed tests:" + sed -n "${BEGIN},${END}p" /tmp/metacall-test-output + fi + + rm /tmp/metacall-test-output + fi +} + +# Build MetaCall Docker Compose for coverage (link manually dockerignore files) +sub_coverage() { + # Disable BuildKit as workaround due to log limits (TODO: https://github.com/docker/buildx/issues/484) + export DOCKER_BUILDKIT=0 + + # Disable build with sanitizer + export METACALL_BUILD_SANITIZER= + + # Disable build with coverage + export METACALL_BUILD_COVERAGE=coverage + + # Define build type + export METACALL_BUILD_TYPE=debug + + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm deps + + ln -sf tools/dev/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.test.yml build --force-rm dev } # Build MetaCall Docker Compose with caching (link manually dockerignore files) @@ -80,33 +186,62 @@ sub_cache() { exit 1 fi - ln -sf tools/node/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build deps_node + ln -sf tools/deps/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build deps + + ln -sf tools/dev/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build dev + + ln -sf tools/runtime/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build runtime + + ln -sf tools/cli/.dockerignore .dockerignore + $DOCKER_COMPOSE -f docker-compose.yml -f docker-compose.cache.yml build cli +} + +# Build MetaCall Docker Compose with multi-platform specifier (link manually dockerignore files) +sub_platform() { + if [ -z "$METACALL_PLATFORM" ]; then + echo "Error: METACALL_PLATFORM variable not defined" + exit 1 + fi + + # Initialize QEMU for Buildkit + docker run --rm --privileged tonistiigi/binfmt --install all + + # Debian in Docker Hub does not support LoongArch64 yet, let's use official LoongArch repository instead + if [ "$METACALL_PLATFORM" = "linux/loong64" ]; then + source .env + export METACALL_BASE_IMAGE="ghcr.io/loong64/${METACALL_BASE_IMAGE}" + fi + + # Generate the docker compose file with all .env variables substituted (bake seems not to support this) + $DOCKER_COMPOSE -f docker-compose.yml config &> docker-compose.bake.yml - ln -sf tools/base/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build deps + # Build with Bake, so the image can be loaded into local docker context + ln -sf tools/deps/.dockerignore .dockerignore + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load deps ln -sf tools/dev/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build dev + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load dev - ln -sf tools/core/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build runtime + ln -sf tools/runtime/.dockerignore .dockerignore + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load runtime ln -sf tools/cli/.dockerignore .dockerignore - docker-compose -f docker-compose.yml -f docker-compose.cache.yml build cli + docker buildx bake -f docker-compose.bake.yml --set *.platform="${METACALL_PLATFORM}" --load cli + + # Delete temporal docker compose file + rm -rf docker-compose.bake.yml } # Push MetaCall Docker Compose -sub_push(){ +sub_push() { if [ -z "$IMAGE_NAME" ]; then echo "Error: IMAGE_NAME variable not defined" exit 1 fi - # Push deps_node image - docker tag metacall/core:deps_node $IMAGE_NAME:deps_node - docker push $IMAGE_NAME:deps_node - # Push deps image docker tag metacall/core:deps $IMAGE_NAME:deps docker push $IMAGE_NAME:deps @@ -128,8 +263,38 @@ sub_push(){ docker push $IMAGE_NAME:latest } +# Version MetaCall Docker Compose +sub_version() { + if [ -z "$IMAGE_NAME" ]; then + echo "Error: IMAGE_NAME variable not defined" + exit 1 + fi + + VERSION=$(tail -n 1 VERSION | tr -d '\n') + + # Push deps image + docker tag metacall/core:deps $IMAGE_NAME:${VERSION}-deps + docker push $IMAGE_NAME:${VERSION}-deps + + # Push dev image + docker tag metacall/core:dev $IMAGE_NAME:${VERSION}-dev + docker push $IMAGE_NAME:${VERSION}-dev + + # Push runtime image + docker tag metacall/core:runtime $IMAGE_NAME:${VERSION}-runtime + docker push $IMAGE_NAME:${VERSION}-runtime + + # Push cli image + docker tag metacall/core:cli $IMAGE_NAME:${VERSION}-cli + docker push $IMAGE_NAME:${VERSION}-cli + + # Push cli image as version + docker tag metacall/core:cli $IMAGE_NAME:${VERSION} + docker push $IMAGE_NAME:${VERSION} +} + # Pack MetaCall Docker Compose -sub_pack(){ +sub_pack() { if [ -z "$ARTIFACTS_PATH" ]; then echo "Error: ARTIFACTS_PATH variable not defined" exit 1 @@ -164,7 +329,13 @@ sub_help() { echo " pull" echo " build" echo " rebuild" + echo " test" + echo " test-address-sanitizer" + echo " test-thread-sanitizer" + echo " test-memory-sanitizer" + echo " coverage" echo " cache" + echo " platform" echo " push" echo " pack" echo "" @@ -180,12 +351,36 @@ case "$1" in rebuild) sub_rebuild ;; + test) + sub_test + ;; + test-address-sanitizer) + export METACALL_BUILD_SANITIZER="address-sanitizer" + sub_test_sanitizer + ;; + test-thread-sanitizer) + export METACALL_BUILD_SANITIZER="thread-sanitizer" + sub_test_sanitizer + ;; + test-memory-sanitizer) + export METACALL_BUILD_SANITIZER="memory-sanitizer" + sub_test_sanitizer + ;; + coverage) + sub_coverage + ;; cache) sub_cache ;; + platform) + sub_platform + ;; push) sub_push ;; + version) + sub_version + ;; pack) sub_pack ;; diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000000..5802aeef51 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,34 @@ +# +# MetaCall Library by Parra Studios +# Docker compose infrastructure for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: "3.7" + +services: + deps: + image: metacall/core:deps + build: + args: + METACALL_BUILD_TYPE: ${METACALL_BUILD_TYPE} + METACALL_INSTALL_OPTIONS: base python ruby netcore8 nodejs typescript file rpc wasm java c cobol go rust rapidjson pack backtrace sandbox ${METACALL_BUILD_COVERAGE} # clangformat v8rep51 + dev: + image: metacall/core:dev + build: + args: + METACALL_BUILD_TYPE: ${METACALL_BUILD_TYPE} + METACALL_BUILD_OPTIONS: ${METACALL_BUILD_SANITIZER} python ruby netcore8 nodejs typescript file rpc wasm java c cobol go rust examples tests scripts ports install pack sandbox benchmarks ${METACALL_BUILD_COVERAGE} # v8 diff --git a/docker-compose.yml b/docker-compose.yml index 1430b5c865..1c1997b138 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker compose infrastructure for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,102 +17,93 @@ # limitations under the License. # -version: "3.7" - services: - deps_node: - image: metacall/core:deps_node - container_name: metacall_core_deps_node - build: - context: . - dockerfile: tools/node/Dockerfile - args: - METACALL_BASE_IMAGE: $METACALL_BASE_IMAGE - METACALL_PATH: $METACALL_PATH - METACALL_NODE_BUILD_TYPE: $METACALL_NODE_BUILD_TYPE - environment: - DEBIAN_FRONTEND: noninteractive - deps: image: metacall/core:deps container_name: metacall_core_deps build: + network: "host" context: . - dockerfile: tools/base/Dockerfile + dockerfile: tools/deps/Dockerfile args: METACALL_BASE_IMAGE: $METACALL_BASE_IMAGE METACALL_PATH: $METACALL_PATH METACALL_TOOLS_PATH: $METACALL_PATH/tools - METACALL_INSTALL_OPTIONS: root base python ruby netcore v8rep51 nodejs rapidjson funchook swig pack # coverage + METACALL_BUILD_TYPE: $METACALL_BUILD_TYPE + METACALL_INSTALL_OPTIONS: base python ruby nodejs typescript file rpc rapidjson pack backtrace # clangformat v8rep51 coverage environment: DEBIAN_FRONTEND: noninteractive + # Work around https://github.com/dotnet/cli/issues/1582 until Docker releases a + # fix (https://github.com/docker/docker/issues/20818). This workaround allows + # the container to be run with the default seccomp Docker settings by avoiding + # the restart_syscall made by LTTng which causes a failed assertion. LTTNG_UST_REGISTER_TIMEOUT: 0 + # Trigger the population of the local NuGet package cache NUGET_XMLDOC_MODE: skip - depends_on: - - deps_node + # Disable NetCore telemetry + DOTNET_CLI_TELEMETRY_OPTOUT: "true" dev: image: metacall/core:dev container_name: metacall_core_dev build: + network: "host" context: . dockerfile: tools/dev/Dockerfile args: METACALL_PATH: $METACALL_PATH - METACALL_BUILD_TYPE: $METACALL_CORE_BUILD_TYPE - METACALL_BUILD_OPTIONS: root python ruby netcore v8 nodejs examples distributable tests scripts ports dynamic install pack # coverage + METACALL_BUILD_TYPE: $METACALL_BUILD_TYPE + METACALL_BUILD_OPTIONS: python ruby nodejs typescript file rpc examples tests scripts ports install pack # v8 coverage benchmarks environment: DEBIAN_FRONTEND: noninteractive LTTNG_UST_REGISTER_TIMEOUT: 0 NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" LOADER_LIBRARY_PATH: $METACALL_PATH/build LOADER_SCRIPT_PATH: $METACALL_PATH/build/scripts CONFIGURATION_PATH: $METACALL_PATH/build/configurations/global.json SERIAL_LIBRARY_PATH: $METACALL_PATH/build DETOUR_LIBRARY_PATH: $METACALL_PATH/build - PORT_LIBRARY_PATH: $METACALL_PATH/build - depends_on: - - deps + NODE_PATH: /usr/lib/node_modules runtime: image: metacall/core:runtime container_name: metacall_core_runtime build: + network: "host" context: . - dockerfile: tools/core/Dockerfile + dockerfile: tools/runtime/Dockerfile args: METACALL_PATH: $METACALL_PATH METACALL_BASE_IMAGE: $METACALL_BASE_IMAGE - METACALL_RUNTIME_OPTIONS: root base python ruby netcore nodejs v8 ports clean + METACALL_RUNTIME_OPTIONS: base python ruby nodejs typescript file rpc backtrace ports clean # v8 environment: DEBIAN_FRONTEND: noninteractive LTTNG_UST_REGISTER_TIMEOUT: 0 NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" LOADER_LIBRARY_PATH: /usr/local/lib LOADER_SCRIPT_PATH: /usr/local/scripts CONFIGURATION_PATH: /usr/local/share/metacall/configurations/global.json SERIAL_LIBRARY_PATH: /usr/local/lib DETOUR_LIBRARY_PATH: /usr/local/lib - PORT_LIBRARY_PATH: /usr/local/lib - depends_on: - - dev + NODE_PATH: /usr/local/lib/node_modules cli: image: metacall/core:cli container_name: metacall_core_cli build: + network: "host" context: . dockerfile: tools/cli/Dockerfile environment: DEBIAN_FRONTEND: noninteractive LTTNG_UST_REGISTER_TIMEOUT: 0 NUGET_XMLDOC_MODE: skip + DOTNET_CLI_TELEMETRY_OPTOUT: "true" LOADER_LIBRARY_PATH: /usr/local/lib LOADER_SCRIPT_PATH: /usr/local/scripts CONFIGURATION_PATH: /usr/local/share/metacall/configurations/global.json SERIAL_LIBRARY_PATH: /usr/local/lib DETOUR_LIBRARY_PATH: /usr/local/lib - PORT_LIBRARY_PATH: /usr/local/lib - depends_on: - - dev - - runtime + NODE_PATH: /usr/local/lib/node_modules diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..55c101c5cd --- /dev/null +++ b/docs/README.md @@ -0,0 +1,884 @@ +
+ M E T A C A L L +

M E T A C A L L

+

A library for providing inter-language foreign function interface calls

+
+ +# Abstract + +**METACALL** is a library that allows calling functions, methods or procedures between programming languages. With **METACALL** you can transparently execute code from/to any programming language, e.g. by calling a Python function from NodeJS: + +`sum.py` + +```python +def sum(a, b): + return a + b +``` + +`main.js` + +```javascript +const { sum } = require("sum.py"); + +sum(3, 4); // 7 +``` + +Use the [installer](https://github.com/metacall/install) and try [some examples](#43-examples). + +
+ M E T A C A L L +
+ +# Table of Contents + + + +- [Abstract](#abstract) +- [Table of Contents](#table-of-contents) + - [1. Motivation](#1-motivation) + - [2. Language Support](#2-language-support) + - [2.1 Loaders (Backends)](#21-loaders-backends) + - [2.2 Ports (Frontends)](#22-ports-frontends) + - [3. Use Cases](#3-use-cases) + - [3.1 Known Projects Using MetaCall](#31-known-projects-using-metacall) + - [4. Usage](#4-usage) + - [4.1 Installation](#41-installation) + - [4.2 Environment Variables](#42-environment-variables) + - [4.3 Examples](#43-examples) + - [5. Architecture](#5-architecture) + - [5.1 Overview](#51-overview) + - [5.1.1 Design Decisions](#511-design-decisions) + - [5.1.2 Modules](#512-modules) + - [5.2 Reflect](#52-reflect) + - [5.2.1 Type System](#521-type-system) + - [5.2.2 Values](#522-values) + - [5.2.3 Functions](#523-functions) + - [5.3 Plugins](#53-plugins) + - [5.3.1 Loaders](#531-loaders) + - [5.3.1.1 Python](#5311-python) + - [5.3.1.2 NodeJS](#5312-nodejs) + - [5.3.1.3 JavaScript](#5313-javascript) + - [5.3.1.4 C#](#5314-c) + - [5.3.1.5 Ruby](#5315-ruby) + - [5.3.1.6 Mock](#5316-mock) + - [5.3.1.7 File](#5317-file) + - [5.3.2 Serials](#532-serials) + - [5.3.2.1 MetaCall](#5321-metacall) + - [5.3.2.2 RapidJSON](#5322-rapidjson) + - [5.3.3 Detours](#533-detours) + - [5.3.3.1 PLTHook](#5331-plthook) + - [5.4 Ports](#54-ports) + - [5.5 Serialization](#55-serialization) + - [5.6 Memory Layout](#56-memory-layout) + - [5.7 Fork Model](#57-fork-model) + - [5.8 Threading Model](#58-threading-model) + - [5. Application Programming Interface (API)](#5-application-programming-interface-api) + - [6. Build System](#6-build-system) + - [6.1 Build Options](#61-build-options) + - [6.2 Coverage](#62-coverage) + - [6.3 Debugging](#63-debugging) + - [6.4 Build on Cloud - Gitpod](#64-build-on-cloud---gitpod) + - [7. Platform Support](#7-platform-support) + - [7.1 Docker Support](#71-docker-support) + - [7.1.1 Docker Development](#711-docker-development) + - [7.1.2 Docker Testing](#712-docker-testing) + - [8. Benchmarks](#8-benchmarks) + - [9. License](#9-license) + + + +## 1. Motivation + +The **METACALL** project started a long time ago when I was coding a [Game Engine for an MMORPG](https://bitbucket.org/parrastudios/argentum-online-c). My idea was to provide an interface to allow other programmers to extend the Game Engine easily. By that time, I was finishing university so I decided to do my [Final Thesis](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-paper.pdf) and [Presentation](https://bitbucket.org/parrastudios/argentum-online-c/raw/e6e78fef80c6adc541640d68d422721ef735184f/common/doc/Plugin/plugin-framework-presentation.pdf) based on the plug-in system for my Game Engine. The Plugin Architecture designed for the Game Engine has similarities with **METACALL** although the architecture has been redefined and the code has been rewritten from scratch. After some refinement of the system, I came up with **METACALL** and other use cases for the tool. Currently we are using **METACALL** to build a cutting edge FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on this technique to provide high scalability of the functions among multiple cores and **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern, a new technique I have developed to transparently interconnect functions in a distributed system based on this library. + +## 2. Language Support + +This section describes all programming languages that **METACALL** supports. **METACALL** is offered through a C API. This means you can use it as a library to embed different runtimes into C. The **[Loaders](#21-loaders-backends)** are the ones that allow to call different functions from C. They are plugins (libraries) that **METACALL** loads and they have a common interface. They usually implement JITs, VMs or interpreters. On the other hand we have the **[Ports](#22-ports-frontends)** which are wrappers to the **METACALL** C API that expose the API to other languages. With the Python Loader we can execute calls to Python from C. With the Python Port we can install **METACALL** via `pip` and use it to call other languages from Python. The combination of both provides the opportunity for complete interoperability between virtually any two languages. + +### 2.1 Loaders (Backends) + +This section describes all programming languages that **METACALL** allows to load and invoke from C language, in other words all languages that **METACALL** can embed. If you are interested in design and implementation details of the loaders, please go to [loaders section](#531-loaders). + +- Currently supported languages and run-times: + +| Language | Runtime | Version | Tag | +| ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------: | :--: | +| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **>= 3.2 <= 3.9** | py | +| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **>= 10.22.0 <= 17.x.x** | node | +| [TypeScript](https://www.typescriptlang.org/) | [TypeScript Language Service API](https://github.com/microsoft/TypeScript/wiki/Using-the-Language-Service-API) | **4.2.3** | ts | +| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [V8](https://v8.dev/) | **5.1.117** | js | +| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/docs/blob/master/docs/core/tutorials/netcore-hosting.md) | **>= 1.0.0-preview2 <= 7.0.4** | cs | +| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **>= 2.1 <= 2.7** | rb | +| [Cobol](https://sourceforge.net/projects/open-cobol/) | [GNU/Cobol](https://open-cobol.sourceforge.io/doxygen/gnucobol-2/libcob_8h.html) | **>= 1.1.0** | cob | +| [File](/source/loaders/file_loader) | **∅** | **0.1.0** | file | +| [Mock](/source/loaders/mock_loader) | **∅** | **0.1.0** | mock | +| [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) | [cURL](https://curl.haxx.se/) | **>=7.64.0** | rpc | +| [Java](https://www.java.com) | [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine) | **>=11** | java | +| [WebAssembly](https://webassembly.org/) | [Wasmtime](https://github.com/bytecodealliance/wasmtime) | **>= 0.27 <= 8.0.1** | wasm | +| [C]() | [libclang](https://clang.llvm.org/doxygen/group__CINDEX.html) - [Tiny C Compiler](https://bellard.org/tcc/) - [libffi](http://sourceware.org/libffi/) | **>=12** - **>=2021-10-30** - **>=3.2** | c | +| [Rust](https://www.rust-lang.org/) | [rustc](https://doc.rust-lang.org/rustc/what-is-rustc.html) - [libffi](http://sourceware.org/libffi/) | **nightly-2021-12-04** | rs | + +- Languages and run-times under construction: + +| Language | Runtime | Tag | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | :--: | +| [C++](http://www.cplusplus.com/) | [Clang](https://clang.llvm.org/) - [LLVM](https://llvm.org/) | cpp | +| [PHP](https://php.net/) | [Zend](https://www.php.net/manual/en/internals2.ze1.zendapi.php) | php | +| [Go](https://golang.org/) | Go Runtime | go | +| [Haskell](https://www.haskell.org/) | [Haskell FFI](https://wiki.haskell.org/GHC/Using_the_FFI) | hs | +| [Crystal](https://crystal-lang.org/) | [Crystal Compiler Internals](https://github.com/crystal-lang/crystal/wiki/Compiler-internals) | cr | +| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [SpiderMonkey](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference) | jsm | +| [Dart](https://dart.dev/) | [Dart VM](https://dart.dev/tools/dart-vm) | dart | +| [LuaJIT](https://luajit.org/) | [LuaJIT2](https://github.com/openresty/luajit2) | lua | +| [LLVM IR](https://www.llvm.org/devmtg/2017-06/1-Davis-Chisnall-LLVM-2017.pdf) | [LLVM](https://llvm.org/) | llvm | +| [Julia](https://julialang.org/) | [Julia Runtime](https://docs.julialang.org/en/v1/devdocs/init/) | jl | + +### 2.2 Ports (Frontends) + +Ports are the frontends to the **METACALL C API** from other languages. They allow to use **METACALL** from different languages. If you are interested in design and implementation details of the ports, please go to [ports section](#54-ports). + +- Currently supported languages and run-times: + +| Language | Runtime | Version | +| ------------------------------------------------------------------ | ---------------------------------------------------------- | :-------------------: | +| [Python](https://www.python.org/) | [Python C API](https://docs.python.org/3/c-api/intro.html) | **3.x** | +| [NodeJS](https://nodejs.org/) | [N API](https://nodejs.org/api/n-api.html) | **>= 8.11.1** | +| [JavaScript](https://developer.mozilla.org/bm/docs/Web/JavaScript) | [D8 (V8)](https://v8.dev/docs/d8) | **5.1.117** | +| [C#](https://dotnet.microsoft.com/) | [NetCore](https://github.com/dotnet/core) | **>= 1.0.0-preview2** | +| [Ruby](https://ruby-lang.org/) | [Ruby C API](https://silverhammermba.github.io/emberb/c/) | **2.x** | +| [Go](https://golang.org/) | [CGO](https://golang.org/cmd/cgo/) | **1.x** | +| [D](https://dlang.org/) | [DMD](https://wiki.dlang.org/DMD) | **2.x** | +| [Rust](https://www.rust-lang.org/) | **∅** | **>= 1.47.0** | +| [Scala](https://www.scala-lang.org/) | [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine) | **>= 2.13.x** | +| [Nim](https://nim-lang.org/) | **∅** | **>= 1.4.2** | + +## 3. Use Cases + +**METACALL** can be used in the following cases: + +- Interconnect different technologies in the same project. It allows heterogeneous teams of developers to work on the same project in an isolated way and use different programming languages at the same time. + +- Embedding programming languages in existing software. Game Engines and 3D Editors like [Blender](https://www.blender.org/), among others can take benefit of **METACALL** and extend the core functionality with higher level programming languages (aka scripting). + +- Function as a Service. **METACALL** can be used to implement efficient FaaS architectures. We are using it to implement our own FaaS (Function as a Service) **[https://metacall.io](https://metacall.io/)** based on **[Function Mesh](https://medium.com/@metacall/function-mesh-architecture-c0304ba4bad0)** pattern and high performance function scalability thanks to this library. + +- Source code migrations. **METACALL** can wrap large and legacy codebases, and provide an agnostic way to work with the codebase in a new programming language. Eventually the code can be migrated in parts, without needing to create a new project or stop the production environment. Incremental changes can be made, solving the migration easily and with less time and effort. + +- Porting low level libraries to high level languages transparently. With **METACALL** you can get rid of extension APIs like Python C API or NodeJS N-API. You can call low level libraries directly from your high level languages without making a wrapper in C or C++ for it. + +As you can see, there are plenty of uses. **METACALL** introduces a new model of programming which allows high interoperability between technologies. If you find any other use case just let us know about it with a Pull Request and we will add it to the list. + +## 3.1 Known Projects Using MetaCall + +- **[Acid Cam](https://www.facebook.com/AcidCam/)**: A software for video manipulation that distorts videos for generating art by means of OpenCV. [Acid Cam CLI](https://github.com/lostjared/acidcam-cli) uses **METACALL** to allow custom filters written in Python and easily embed Python programming language into its plugin system. + +- **[Pragma](https://pragmalang.com/)**: Pragma is a language for building beautiful and extensible GraphQL APIs in no time. Within a single file, you can define your data models and authorization rules (roles and permissions), and import serverless functions for data validation, transformation, authorization, or any custom logic. [Pragma](https://github.com/pragmalang/pragma) uses **METACALL** to import and execute functions into the language so it can be extended with other programming languages. + +## 4. Usage + +## 4.1 Installation + +Before trying any of the examples, you must have **METACALL** installed in your system. To install **METACALL** you have the following options: + +- [Install precompiled tarball via shell script (downloads the tarball generated by Guix)](https://github.com/metacall/install). +- [Build and install it manually](#6-build-system). +- [Pull it from DockerHub](https://hub.docker.com/r/metacall/core). +- [Install via Guix package manager](https://github.com/metacall/distributable/blob/master/source/metacall.scm) (needs to fix the commit of [Guix channels](https://github.com/metacall/distributable/blob/master/channels/channels.scm)). +- [Download the precompiled tarball from Guix via Distributable Releases Assets](https://github.com/metacall/distributable/releases). +- [ArchLinux AUR](https://github.com/metacall/aur). +- [Homebrew](https://github.com/metacall/homebrew). +- [Download precompiled tarball (.tar.gz) or Debian (.deb) / RPM (.rpm) installers via Core Releases Assets](https://github.com/metacall/core/releases). + +### 4.2 Environment Variables + +The environment variables are optional, in case you want to modify default paths of **METACALL**. + +| Name | Description | Default Value | +| :-----------------------: | ---------------------------------------------------------------- | :------------------------------: | +| **`DETOUR_LIBRARY_PATH`** | Directory where detour plugins to be loaded are located | **`detours`** | +| **`SERIAL_LIBRARY_PATH`** | Directory where serial plugins to be loaded are located | **`serials`** | +| **`CONFIGURATION_PATH`** | File path where the **METACALL** global configuration is located | **`configurations/global.json`** | +| **`LOADER_LIBRARY_PATH`** | Directory where loader plugins to be loaded are located | **`loaders`** | +| **`LOADER_SCRIPT_PATH`** | Directory where scripts to be loaded are located | **`${execution_path}`** ¹ | + +¹ **`${execution_path}`** defines the path where the program is executed, **`.`** in Linux. + +### 4.3 Examples + +- [BeautifulSoup from Express](https://github.com/metacall/beautifulsoup-express-example): This example shows how to use [**METACALL** CLI](/source/cli/metacallcli) for building a **Polyglot Scraping API** that mixes NodeJS with Python. + +- [Higher Order Functions with Python & NodeJS](https://github.com/metacall/fn.py-javascript-example): An example of using [Fn.py](https://github.com/kachayev/fn.py) (Python) from JavaScript (NodeJS). + +- [Embedding NodeJS](https://github.com/metacall/embedding-nodejs-example): Example application for embedding NodeJS code into C/C++ using CMake as a build system. + +- [Embedding Python](https://github.com/metacall/embedding-python-example): Example application for embedding Python code into C/C++ using CMake as a build system. + +- [Embedding Ruby](https://github.com/metacall/embedding-ruby-example): Example application for embedding Ruby code into C/C++ using CMake as a build system. + +- [Mixing Go and TypeScript](https://github.com/metacall/golang-typescript-example): This example shows how to embed TypeScript into Go using METACALL. In other words, calling TypeScript functions from Go. + +- [Using `matplotlib` from C/C++](https://github.com/metacall/embedding-matplotlib-example): Example application for using Python `matplotlib` library into C/C++ using `gcc` for compiling it and installing **METACALL** by compining it by hand. + +- [Polyglot Redis Module](https://github.com/metacall/redis-module): Extend Redis DataBase modules with TypeScript, JavaScript, Python, C#, Ruby... + +- [Rotulin](https://github.com/metacall/rotulin): Example of a multi-language application built with **METACALL**. This application embeds a Django server with a Ruby DataBase and C# business layer based on ImageMagick. + +## 5. Architecture + +### 5.1 Overview + +#### 5.1.1 Design Decisions + +- To provide a high level API with a simple UX and to be easy to understand. + +- To work in high performance environments. + +- To be as cross-platform as possible. + +- To avoid modifying run-times directly or using the code inside **METACALL** to avoid maintaining them, or propagating security flaws or licenses into **METACALL**. + +- To provide support for any embeddable programming language and to provide support for **METACALL** to be used from any programming language. + +- All external code used in **METACALL** must be introduced by inversion of control in the plugin system so that the core must not remain aware of what software is used. + +- All code developed in **METACALL** must be implemented in standalone libraries that can work by themselves in an isolated way (aka modules). + +#### 5.1.2 Modules + +- [`adt`](/source/adt) provides a base for Abstract Data Types and algorithms used in **METACALL**. Implementation must be done in an efficient and generic way. Some of the data structures implemented are vector, set, hash, comparable or trie. + +- [`detour`](/source/detour) provides an interface to hook into functions. Detours are used by the [fork model](#57-fork-model) to intercept fork calls. + +- [`detours`](/source/detours) implement the [`detour`](/source/detour) interface by using a plugin architecture. The current list of available detour plugins is the following one. + + - [`plthook_detour`](/source/detours/plthook_detour) implemented by means of PLTHook library. + +- [`dynlink`](/source/dynlink) implements a cross-platform method to dynamically load libraries. It is used to dynamically load plugins into **METACALL**. + +- [`environment`](/source/environment) implements a standard way to deal with environment variables. **METACALL** uses environment variables to define custom paths for plugins and scripts. + +- [`examples`](/source/examples) ... + +- [`filesystem`](/source/filesystem) provides an abstraction for operative system file system. + +- [`format`](/source/format) provides a standard way for printing to standard input output for old C versions that does not support newest constructions. + +- [`loader`](/source/loader) ... + +- [`loaders`](/source/loaders) + +- [`log`](/source/log) + +- [`memory`](/source/memory) + +- [`metacall`](/source/metacall) + +- [`ports`](/source/ports) + +- [`preprocessor`](/source/preprocessor) + +- [`reflect`](/source/reflect) + +- [`scripts`](/source/scripts) + +- [`serial`](/source/serial) + +- [`serials`](/source/serials) + +- [`tests`](/source/tests) + +- [`version`](/source/version) + +### 5.2 Reflect + +The module that holds the representation of types, values and functions is called [`reflect`](/source/reflect) and it handles the abstraction of code loaded into **METACALL**. + +**METACALL** uses reflection and introspection techniques to inspect the code loaded by the [`loaders`](/source/loaders) in order to interpret it and provide a higher abstraction of it. With this higher abstraction **METACALL** can easily inter-operate between languages transparently. + +#### 5.2.1 Type System + +**METACALL** implements an abstract type system which is a binary representation of the types supported by it. This means that **METACALL** can convert any type of a language to its own type system and back. Each loader is responsible of doing these conversions. + +**METACALL** maintains most of the types of the languages but not all are supported. If new types are added they have to be implemented in the [`reflect`](/source/reflect) module and also in the [`loaders`](/source/loaders) and [`serials`](/source/serials) to fully support it. + +| Type | Value | +| :------: | ------------------------------------------------------------------------------ | +| Boolean | `true` or `false` | +| Char | `-128` to `127` | +| Short | `-32,768` to `32,767` | +| Int | `-2,147,483,648` to `2,147,483,647` | +| Long | `–9,223,372,036,854,775,808` to `9,223,372,036,854,775,807` | +| Float | `1.2E-38` to `3.4E+38` | +| Double | `2.3E-308` to `1.7E+308` | +| String | NULL terminated list of characters | +| Buffer | Blob of memory representing a binary data | +| Array | Arrangement of values of any type | +| Map | List of elements formed by a key (String) value (Any) pair (Array) | +| Pointer | Low level representation of a memory reference | +| Null | Representation of NULL value type | +| Future | Promise in Node Loader, and any other type equivalent in other languages. | +| Function | Block of code that takes inputs (Arguments) and produces output (Return value) | +| Class | Defines properties and methods that are common to all objects | +| Object | An instance of Class | + +- Boolean is mostly represented by an integer value. There are languages that does not support it so it gets converted to an integer value in the memory layout. + +- Integer and Floating Point values provide a complete abstraction to numerical types. Type sizes are preserved and the correct type is used when using any number. This depends on the internal implementation of the value by the run-time. Although there can be problems related to this. A `bignum` type from Ruby may overflow if it is too big when trying to convert it to a `float` type in C#. + +- String is represented by ASCII encoding currently. Future versions will implement multiple encodings to be interoperable between other language encodings. + +- Buffer represents a blob of raw memory (i.e. an array of bytes). This can be used to represent files as images or any other resources into memory. + +- Array is implemented using an array of values, which might lead one to ponder that it should be called _list_ instead. But as the memory layout is stored into a contiguous memory block of references to values, it is considered an array. + +- Map implements an associative key value pair container. A map is implemented with an array of two sized elements array. Each element of the map is an array of size two, where the first element of it is always a String and the second element is a value of any type. + +- Pointer is an opaque value representing a raw reference to a memory block. Some languages allow to use references to memory and some others do not. This type is opaque because **METACALL** does not know what kind of concrete value represents it. The representation may be a complex type handled by the developer source code inside the run-time. + +- Null type implements a null value. This type has only been implemented in order to support null value from multiple run-times. It represents a null value and it does not have data size on the value allocated. + +#### 5.2.2 Values + +Values represent the instances of the **METACALL** type system. + +The memory layout guarantees to fit at least the same size of the types into memory. This means if a boolean type can be represented with one bit inside a value of one byte size, maybe this value is stored in a bigger memory block and this fact is architecture and platform dependant. + +When converting values between different types, if any potential number overflow or invalid conversion between types is done, **METACALL** will warn about it. If any conversion of types can be handled by **METACALL**, it will automatically cast or transform the values into the target type automatically in order to avoid errors in the call. + +The value model is implemented by means of object pool. Each value is a reference to a memory block allocated from a memory pool (which can be injected into **METACALL**). The references can be passed by value, this means **METACALL** copies the reference value instead of the data which this reference is pointing to, like most run-times do when managing their own values. + +Each created value must be destroyed manually, otherwise it will lead to a memory leak. This only occurs when dealing with **METACALL** at C level. If **METACALL** is being used in a higher language through [`ports`](/source/ports), the developer does not have to care about memory management. + +The value memory layout is described in the following form. + +| Memory Offset | `0` to `sizeof(data) - 1` | `sizeof(data)` to `sizeof(data) + sizeof(type_id) - 1` | +| :-----------: | :-----------------------: | :----------------------------------------------------: | +| **Content** | **DATA** | **TYPE ID** | + +This layout is used for the following reasons: + +- Data is located at the first position of the memory block, so it can be used as a normal low level value. This allows to treat **METACALL** values as normal C values. Therefore you can use **METACALL** with normal pointers to existing variables, literal values as shown in the previous examples or **METACALL** values. + +- Data can be accessed faster as it is located at first position of the memory block. There is not extra calculation of an offset when trying to access the pointer. + +- Data and type id are contiguously allocated in order to treat it as the same memory block so it can be freed with one operation. + +#### 5.2.3 Functions + +Functions are abstract callable representations of functions, methods or procedures loaded by [`loaders`](/source/loaders). A function is like a template which is linked to a loader run-time and allows to do a foreign function call. + +A function is composed of a name and a signature. The signature defines the arguments names, types, and return type, if any. When a function is loaded, **METACALL** tries to inspect the signature and records the types, if any. It stores the arguments names and sizes and also a concrete type that will be used later by the loader to implement the call to the run-time. + +The function interface must be implemented by the [`loaders`](/source/loaders) and it has the following form. + +```c +typedef struct function_interface_type +{ + function_impl_interface_create create; + function_impl_interface_invoke invoke; + function_impl_interface_await await; + function_impl_interface_destroy destroy; + +} * function_interface; +``` + +- `create` instantiates the function concrete data related to the run-time. +- `invoke` transforms arguments from [`reflect`](/source/reflect) abstract types to run-time concrete types, executes the call in the run-time, and converts the result of the call from run-time concrete type to [`reflect`](/source/reflect) abstract type. +- `await` idem to invoke but awaiting the promise that is expected to be returned by the function. +- `destroy` clears all data previously instantiated in `create`. + +The type deduction can be done at different levels. For example, it is possible to guess function types from the loaded code. + +```python +def multiply_type(a: int, b: int) -> int: + return a * b +``` + +If this code is loaded, **METACALL** will be able to inspect the types and define the signature. Signature includes the names of the arguments, the types of those arguments, if any, and the return type, if any. + +It may be possible that the function loaded into **METACALL** is duck typed. This means it does not have information about what types it supports and therefore they cannot be inspected statically. + +```python +def multiply_duck(a, b): + return a * b +``` + +At low level **METACALL** must always know the types to do the call. This types can be inferred statically or dynamically and this has implications over the call model. + +In the first example, we can simply call the function without specifying the types. + +```c +metacall("multiply_type", 3, 4); // 12 +``` + +As the signature is already know the literal values `3` and `4` can be converted into **METACALL** values automatically. Note that in this case, as literal values are provided, if we pass a double floating point, the memory representation of the value will be corrupted as there is no possible way to detect input values and cast them to the correct target values. + +In the second example, the values are not know. If we use the same API to call the function, **METACALL** will not be able to call correctly the function as its types are not know. To allow calls to duck typed functions the developer must specify the value types he is passing to the function. + +```c +const enum metacall_value_id multiply_types[] = +{ + METACALL_INT, METACALL_INT +}; + +metacallt("multiply_duck", multiply_types, 3, 4); // 12 +``` + +This method allows to pass different value types to the same function. The following call would be valid too. + +```c +const enum metacall_value_id multiply_types[] = +{ + METACALL_DOUBLE, METACALL_DOUBLE +}; + +metacallt("multiply_duck", multiply_types, 3.0, 4.0); // 12.0 +``` + +### 5.3 Plugins + +**METACALL** has a plugin architecture implemented at multiple levels. + +- Loaders implement a layer of plugins related to the run-times. + +- Serials implement a layer of (de)serializers in order to transform input (arguments) or output (return value) of the calls into a generic format. + +- Detours is another layer of plugins focused on low level function interception (hooks). + +Each plugin is a piece of software that can be dynamically loaded into the **METACALL** core, used and unloaded when it is not needed anymore. + +#### 5.3.1 Loaders + +Loaders are responsible for embedding run-times into **METACALL**. Each loader has the following interface. + +```c +typedef struct loader_impl_interface_type +{ + loader_impl_interface_initialize initialize; + loader_impl_interface_execution_path execution_path; + loader_impl_interface_load_from_file load_from_file; + loader_impl_interface_load_from_memory load_from_memory; + loader_impl_interface_load_from_package load_from_package; + loader_impl_interface_clear clear; + loader_impl_interface_discover discover; + loader_impl_interface_destroy destroy; + +} * loader_impl_interface; +``` + +A loader must implement it to be considered a valid loader. + +- `initialize` starts up the run-time. +- `execution_path` defines a new import path to the run-time. +- `load_from_file` loads a code from file into the run-time and returns a handle which represents it. +- `load_from_memory` loads a code from memory into the run-time and returns a handle which represents it. +- `load_from_package` loads a code from a compiled library or package into the run-time and returns a handle which represents it. +- `clear` unloads a handle from the run-time. +- `discover` inspects a handle previously loaded. +- `destroy` shutdowns the run-time. + +##### 5.3.1.1 Python + +##### 5.3.1.2 NodeJS + +##### 5.3.1.3 JavaScript + +##### 5.3.1.4 C# + +##### 5.3.1.5 Ruby + +##### 5.3.1.6 Mock + +##### 5.3.1.7 File + +#### 5.3.2 Serials + +##### 5.3.2.1 MetaCall + +##### 5.3.2.2 RapidJSON + +#### 5.3.3 Detours + +##### 5.3.3.1 PLTHook + +### 5.4 Ports + +### 5.5 Serialization + +### 5.6 Memory Layout + +### 5.7 Fork Model + +**METACALL** implements a fork safe model. This means if **METACALL** is running in any program instance, the process which is running can be forked safely at any moment of the execution. This fact has many implications at design, implementation and use levels. But the whole **METACALL** architecture tries to remove all responsibility from the developer and make this transparent. + +To understand the **METACALL** fork model, first of all we have to understand the implications of the forking model in operative systems and the difference between [fork-one and fork-all models](https://docs.oracle.com/cd/E37838_01/html/E61057/gen-1.html). + +The main difference between fork-one and fork-all is that in fork-one only the thread which called the fork is preserved after the fork (i.e. gets cloned). In fork-all model, all threads are preserved after cloning. POSIX uses fork-one model, meanwhile Oracle Solaris use the fork-all model. + +Because of fork-one model, forking a running run-time like NodeJS (which has a thread pool) implies that in the child process the thread pool will be almost dead except the thread which did the fork call. So NodeJS run-time cannot continue the execution anymore and the event-loop enters into a deadlock state. + +When a fork is done, the status of the execution is lost by the moment. **METACALL** is not able to preserve the state when a fork is done. Some run-times do not allow to preserve the internal state. For example, the bad design[[0]](https://github.com/nodejs/node/issues/23265)[[1]](https://github.com/nodejs/node/issues/23265#issuecomment-452690239)[[2]](https://github.com/nodejs/node/issues/23265#issuecomment-496873739)[[3]](https://github.com/nodejs/node/issues/23265#issuecomment-496878712)[[4]](https://github.com/nodejs/node/issues/23265#issuecomment-496910654)[[5]](https://github.com/nodejs/node/issues/23265#issuecomment-496918901) of NodeJS does not allow to manage the thread pool from outside, so it cannot be preserved after a fork. + +Because of these restrictions, **METACALL** cannot preserve the status of the run-times. In the future this model will be improved to maintain consistency and preserve the execution state of the run-times making **METACALL** more robust. + +Although the state is not preserved, fork safety is. The mechanism **METACALL** uses to allow fork safety is described in the following enumeration. + +1. Intercept fork call done by the program where **METACALL** is running. + +2. Shutdown all run-times by means of unloading all loaders. + +3. Execute the real fork function. + +4. Restore all run-times by means of reloading all loaders. + +5. Execute user defined fork callback if any. + +To achieve this, **METACALL** hooks fork primitives depending on the platform. + +- `fork` on POSIX systems. +- `RtlCloneUserProcess` on Windows systems. + +If you use `clone` instead of `fork` to spawn a new process in a POSIX system, **METACALL** won't catch it. + +Whenever you call a to a cloning primitive **METACALL** intercepts it by means of [**`detour`**](/source/detour). Detours is a way to intercept functions at low level by editing the memory and introducing a jump over your own function preserving the address of the old one. **METACALL** uses this method instead of POSIX `pthread_atfork` for three main reasons. + +- The first one is that `pthread_atfork` is only supported by POSIX systems. So it is not a good solution because of the philosophy of **METACALL** is to be as cross-platform as possible. + +- The second is that `pthread_atfork` has a [bug in the design of the standard](https://stackoverflow.com/a/6605487). It was designed to solve a problem which cannot be solved with `pthread_atfork` itself. This means that even having the control of NodeJS thread pool, it will not be possible to restore the [mutexes](https://github.com/nodejs/node/blob/v8.x/src/node_platform.cc) in the child process. The only possibility is to re-implement the thread pool of NodeJS with async safe primitives like a semaphore. Async safe primitives will be able to work in the child process handler. But this is not possible as it enters in conflict with the design decision of to not modify the run-times. + +- The third one is that the mechanism of `pthread_atfork` also [will be deprecated](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html) because of second reason. + > The `pthread_atfork()` function may be formally deprecated (for example, by shading it OB) in a future version of this standard. + +Detours model is not safe. It is platform dependant and implies that the program modifies the memory of itself during the execution which is not safe at all and can induce bugs or security flaws if it is not done correctly. But because of limitations of run-times, there is not another alternative to solve the problem of fork safety. + +Usually the developer is the same who does the fork, but it may be possible that **METACALL** is embedded into a larger application and the developer is in the middle between the application code and **METACALL** so it is impossible to control when a fork is done. Because of this the developer can register a callback by means of [**`metacall_fork`**](/source/metacall/include/metacall/metacall_fork.h) to know when a fork is executed to do the actions needed after the fork, for example, re-loading all previous code and restore the state of the run-times. This gives a partial solution to the problem of losing the state when doing a fork. + +### 5.8 Threading Model + +The threading model is still experimental. We are discovering the best ways of designing and implementing it, so it may vary over time. At the moment of writing (check the commit history), there are some concerns that are already known and parts of the design already achieved thanks to the NodeJS event loop nature. + +The Node Loader is designed in a way in which the V8 instance is created in a new thread, and from there the event loop "blocks" that thread until the execution. Recent versions of N-API (since NodeJS 14.x) allow you to have control and reimplement your own event loop thanks to the new embedder API. But when this project started and NodeJS loader was implemented, only NodeJS 8.x exist. So the only option (without reimplementing part of NodeJS, because it goes against one design decisions of the project) was to use `node::Start`, a call that blocks your thread while executing the event loop. This also produces a lot of problems, because of lack of control over NodeJS, but they are not directly related to the thread model. + +To overcome the blocking nature of `node::Start`, the event loop is launched in a separated thread, and all calls to the loader are executed via submission to the event loop in that thread. In the first implementation, it was done using `uv_async_t`, but in the current implementation (since NodeJS 10.x), with thread safe mechanisms that allow you to enqueue safely into the event loop thanks to the new additions to the N-API. The current thread where the call is done waits with a condition `uv_cond_t` upon termination of the submission and resolution of the call. + +This solution of waiting to the call with the condition, introduces new problems. For completely async calls, there is no problem at all, but for synchronous calls, it can deadlock. For example, when calling recursively to the same synchronous function via **METACALL**, in the second call it will try to block twice and deadlock the thread. So in order to solve this an atomic variable was added in addition to a variable storing the thread id of the V8 thread. With this, recursive calls can be detected, and instead of blocking and enqueueing them, it is possible to call directly and safely to the function because we are already in the V8 thread when the second iteration is done. + +This solves all (known) issues related to NodeJS threading model **if and only if** you use **METACALL** from C/C++ or Rust as a library, and you don't mix languages. This means, you use directly the low level API directly, and you do not use any `Port` or you mix this with other languages, doing calls in between. You can still have a chance to generate deadlocks if your software uses incorreclty the API. For example, you use one condition which gets released in an async callback (a lambda in the argument of the call to `metacall_await`) and your JS code never resolves properly that promise. + +If you use the CLI instead, and your host language is Python or any other (which does not allow to use you the low level API), and you want to load scripts from other languages, you have to use **METACALL** through `Ports`. Ports provide a high abstraction of the low level API and allow you to load and call functions of other languages. Here is where the fun begins. + +There are few considerations we must take into account. In order to explain this we are going to use a simple example first, using Python and NodeJS. Depending on the runtime, there are different mechanisms to handle threads and thread safety: + +- Python: + + 1. Python uses a Global Interpreter Lock (GIL), which can be acquired from different threads in order to do thread safe calls. This can be problematic due to deadlocks. + 2. Python event loop can be decoupled from Python interpreter thread by using Python Thread API (work in progress: https://github.com/metacall/core/pull/64). This fact simplifies the design. + 3. Python can run multiple interpreter instances, starting from newer versions (not implemented yet). + +- NodeJS: + 1. NodeJS uses a submission queue and does not suffer from a global mutex like Python. + 2. NodeJS V8 thread is coupled to the event loop (at least with the current version used in **METACALL**, and it is difficult to have control over it). + 3. NodeJS can execute multiple V8 threads with the multi-isolate library from the latest versions of V8 (not implemented yet). + +Once these concerns are clear, now we can go further and inspect some cases where we can find deadlocks or problems related to them: + +1. **NodeJS is the host language**, and it launches the Python interprer in the V8 thread: + +![Threading Model NodeJS Python](https://github.com/metacall/core/blob/master/docs/diagrams/threading-model-nodejs-python.png) + +This model is relatively safe because Node Loader is completely reentrant, and Python GIL too. This means you can do recursive calls safely, and all those calls will always happen in V8. Even if we do callbacks, all of them will happen in the same thread, so there aren't potential deadlocks. This means we can safely use a functional library from NodeJS, and it won't deadlock. For example: [Using Fn.py from NodeJS](https://github.com/metacall/fn.py-javascript-example). + +But there is a problem when we try to destroy the loaders. Python interpreter does not allow to be destroyed from a different thread where it was launched. This means, if we destroy the Node Loader first, then it will be impossible to destroy the Python Loader, because the V8 thread has been finished. We must destroy the Loaders in order and in the correct thread. This means if we try to destroy Node Loader, during its destruction in the V8 thread, we must destroy Python Loader and any other loader that has been initialized in that thread. + +As a result, each loader must use the following instructions: + +- When the loader has finished the initialization, it must register its initialization order. It will record internally the current thread id too. + + ```c + loader_initialization_register(impl); + ``` + +- When the loader is going to be destroyed, but before destroy starts, the children must be destroyed in a recursive way, so the whole tree can be iterated properly in order. + + ```c + loader_unload_children(); + ``` + +The result of the current destruction model is that: **`metacall_initialize` and `metacall_destroy` must be done from the same thread**. This should not be a problem for developers using the CLI. But embedders must take this into account. + +2. **Python is the host language**, and it launches NodeJS in a new (V8) thread: + [TODO: Explain why callbacks deadlock in this context] + +In order to end this section, here's a list of ideas that are not completely implemented yet, but they are in progress: + +- Lock free data structures for holding the functions. +- Asynchronous non-deadlocking, non-stack growing callbacks between runtimes (running multiple event loops between languages). This will solve the second case where Python is the host language and deadlocks because of NodeJS event loop nature. +- Support for multi-isolate and multiple interpreters instances. + +## 5. Application Programming Interface (API) + +## 6. Build System + +Follow these steps to build and install **METACALL** manually. + +```sh +git clone https://github.com/metacall/core.git +mkdir core/build && cd core/build +cmake .. +# Unix (Linux and MacOs) +sudo HOME="$HOME" cmake --build . --target install +# Windows (or when installing to a path with permissions) +cmake --build . --target install +``` + +### 6.1 Build Options + +These options can be set using **`-D`** prefix when configuring CMake. For example, the following configuration enables the build of Python and Ruby loaders. + +```sh +cmake -DOPTION_BUILD_LOADERS_PY=On -DOPTION_BUILD_LOADERS_RB=On .. +``` + +Available build options are the following ones. + +| Build Option | Description | Default Value | +| :-------------------------: | ------------------------------------------------------ | :-----------: | +| **BUILD_SHARED_LIBS** | Build shared instead of static libraries. | ON | +| **OPTION_SELF_CONTAINED** | Create a self-contained install with all dependencies. | OFF | +| **OPTION_BUILD_TESTS** | Build tests. | ON | +| **OPTION_BUILD_BENCHMARKS** | Build benchmarks. | OFF | +| **OPTION_BUILD_DOCS** | Build documentation. | OFF | +| **OPTION_BUILD_EXAMPLES** | Build examples. | ON | +| **OPTION_BUILD_LOADERS** | Build loaders. | ON | +| **OPTION_BUILD_SCRIPTS** | Build scripts. | ON | +| **OPTION_BUILD_SERIALS** | Build serials. | ON | +| **OPTION_BUILD_DETOURS** | Build detours. | ON | +| **OPTION_BUILD_PORTS** | Build ports. | OFF | +| **OPTION_FORK_SAFE** | Enable fork safety. | OFF | +| **OPTION_THREAD_SAFE** | Enable thread safety. | OFF | +| **OPTION_COVERAGE** | Enable coverage. | OFF | +| **CMAKE_BUILD_TYPE** | Define the type of build. | Release | + +It is possible to enable or disable concrete loaders, script, ports, serials or detours. For building use the following options. + +| Build Option Prefix | Build Option Suffix | +| :-----------------------: | --------------------------------------------------------------------- | +| **OPTION_BUILD_LOADERS_*** | `C` `JS` `CS` `MOCK` `PY` `JSM` `NODE` `RB` `FILE` | +| **OPTION_BUILD_SCRIPTS_*** | `C` `CS` `JS` `NODE` `PY` `RB` `JAVA` | +| **OPTION_BUILD_SERIALS_*** | `METACALL` `RAPID_JSON` | +| **OPTION_BUILD_DETOURS_*** | `PLTHOOK` | +| **OPTION_BUILD_PORTS_*** | `CS` `CXX` `D` `GO` `JAVA` `JS` `LUA` `NODE` `PHP` `PL` `PY` `R` `RB` | + +To format the entire C/C++ codebase use: + +```sh +cmake --build build --target clang-format +``` + +Be aware that this target won't exist if clang-format was not installed when cmake was last run. + +### 6.2 Coverage + +In order to run code coverage and obtain html reports use the following commands (assuming you just clonned the repository): + +```sh +git clone https://github.com/metacall/core.git +mkdir core/build && cd core/build +cmake -DCMAKE_BUILD_TYPE=Debug -DOPTION_COVERAGE=On .. +make -j$(NPROC) +ctest +ctest -T Coverage +gcovr -r ../source/ . --html-details coverage.html +``` + +The output reports will be generated in `${CMAKE_BINARY_DIR}/coverage.html` in html format. + +### 6.3 Debugging + +For debugging memory leaks, undefined behaviors and other related problems, the following compile options are provided: + +| Build Option | Description | Default Value | +| :--------------------------------: | --------------------------------------------------------- | :-----------: | +| **OPTION_TEST_MEMORYCHECK** | Enable Valgrind with memcheck tool for the tests. | OFF | +| **OPTION_BUILD_ADDRESS_SANITIZER** | Build with AddressSanitizer family (GCC, Clang and MSVC). | OFF | +| **OPTION_BUILD_THREAD_SANITIZER** | Build with ThreadSanitizer family (GCC, Clang and MSVC). | OFF | +| **OPTION_BUILD_MEMORY_SANITIZER** | Build with MemorySanitizer family (Clang and MSVC). | OFF | + +All options are mutually exclusive. Valgrind is not compatible with AddressSanitizer and AddressSanitizer is not compatible with ThreadSanitizer and AddressSanitizer with MemorySanitizer. Some run-times may fail if they are not compiled with AddressSanitizer too, for example NetCore. Due to this, tests implying may fail with signal 11. The same problem happens with Valgrind, due to that, some tests are excluded of the memcheck target. + +For running all tests with Valgrind, enable the `OPTION_TEST_MEMORYCHECK` flag and then run: + +```sh +make memcheck +``` + +For runing a test (or all) with AddressSanitizer or ThreadSanitizer, enable the `OPTION_BUILD_ADDRESS_SANITIZER` or `OPTION_BUILD_THREAD_SANITIZER` flags respectively and then run: + +```sh +# Run one test +make py_loader rb_loader node_loader metacall-node-port-test # Build required dependencies and a test +ctest -VV -R metacall-node-port-test # Run one test (verbose) + +# Run all +make +ctest +``` + +For running other Valgrind's tools like helgrind or similar, I recommend running them manually. Just run one test with `ctest -VV -R metacall-node-port-test`, copy the environment variables, and configure the flags by yourself. + +### 6.4 Build on Cloud - Gitpod + +Instead of configuring a local setup, you can also use [Gitpod](https://www.gitpod.io/), an automated cloud dev environment. + +Click the button below. A workspace with all required environments will be created. + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/metacall/core) + +> To use it on your forked repo, edit the 'Open in Gitpod' button url to `https://gitpod.io/#https://github.com//core` + +## 7. Platform Support + +The following platforms and architectures have been tested and are known to work correctly with all plugins of **METACALL**. + +| Operative System | Architecture | Compiler | +| :--------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :---------: | +| **`ubuntu`** | **`amd64`** | **`gcc`** | +| **`debian`** | **`amd64`** **`amd64/v2`** **`amd64/v3`** **`386`** **`arm64`** **`riscv64`** **`ppc64le`** **`arm/v7`** **`arm/v6`** **`loong64`** | **`gcc`** | +| **`macos`** | **`amd64`** **`arm64`** | **`clang`** | +| **`windows`** | **`x86`** **`x64`** | **`msvc`** | + +### 7.1 Docker Support + +To provide a reproducible environment **METACALL** is also distributed under Docker on [DockerHub](https://hub.docker.com/r/metacall/core). Current images are based on `debian:bookworm-slim` for `amd64` architecture. + +For pulling the **METACALL** `latest` image containing the runtime, use: + +```sh +docker pull metacall/core +``` + +For pulling a specific image depending on the tag, use: + +- **METACALL** `deps` image. Includes all dependencies for development: + +```sh +docker pull metacall/core:deps +``` + +- **METACALL** `dev` image. Includes all dependencies, headers and libraries for development: + +```sh +docker pull metacall/core:dev +``` + +- **METACALL** `runtime` image. Includes all dependencies and libraries for runtime: + +```sh +docker pull metacall/core:runtime +``` + +- **METACALL** `cli` image. Includes all dependencies and libraries for runtime and the CLI as entry point (equivalent to `latest`): + +```sh +docker pull metacall/core:cli +``` + +### 7.1.1 Docker Development + +It is possible to develop **METACALL** itself or applications using **METACALL** as standalone library with Docker. The `dev` image can be used for development. It contains all dependencies with all run-times installed with the code, allowing debugging too. + +Use the following commands to start developing with **METACALL**: + +```sh +mkdir -p $HOME/metacall +code $HOME/metacall +``` + +We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. + +```sh +docker pull metacall/core:dev +docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:dev /bin/bash +``` + +Inside docker terminal you can run `python` or `ruby` command to test what you are developing. You can also run `metacallcli` to test (load, clear, inspect and call). + +### 7.1.2 Docker Testing + +An alternative for testing is to use a reduced image that includes the runtime and also the CLI. This alternative allows fast prototyping and CLI management in order to test and inspect your own scripts. + +Use the following commands to start testing with **METACALL**: + +```sh +mkdir -p $HOME/metacall +code $HOME/metacall +``` + +We are going to run a docker container with a mounted volume. This volume will connect the `LOADER_SCRIPT_PATH` inside the container, and your development path in the host. We are using `$HOME/metacall`, where we have our editor opened. + +```sh +docker pull metacall/core:cli +docker run -e LOADER_SCRIPT_PATH=/metacall -v $HOME/metacall:/metacall -w /metacall -it metacall/core:cli +``` + +After the container is up, it is possible to load any script contained in host folder `$HOME/metacall`. If we have a `script.js` inside the folder, we can just load it (each line beginning with `>` is the input command): + +`script.js` + +```js +function sum(left, right) { + return left + right; +} + +module.exports = { + sum, +}; +``` + +`Command Line Interface` + +```sh +> load node script.js +Script (script.js) loaded correctly +> inspect +runtime node { + module script { + function sum(left, right) + } +} +runtime __metacall_host__ +> call sum(3, 5) +8.0 +> exit +``` + +Where `script.js` is a script contained in host folder `$HOME/metacall` that will be loaded on the CLI after starting up the container. Type `help` to see all available CLI commands. + +## 8. Benchmarks + +**METACALL** provides benchmarks for multiple operative systems in order to improve performance iteratively, those can be found in GitHub Pages: + +| Operative System | URL | +| :-----------------: | :--------------------------------------------------: | +| **`ubuntu-latest`** | https://metacall.github.io/core/bench/ubuntu-latest/ | +| **`macos-latest`** | https://metacall.github.io/core/bench/macos-latest/ | +| **`windows-2022`** | https://metacall.github.io/core/bench/windows-2022/ | +| **`windows-2025`** | https://metacall.github.io/core/bench/windows-2025/ | + +## 9. License + +**METACALL** is licensed under **[Apache License Version 2.0](/LICENSE)**. + +> Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia <> +> +> Licensed under the Apache License, Version 2.0 (the "License"); +> you may not use this file except in compliance with the License. +> You may obtain a copy of the License at +> +> http://www.apache.org/licenses/LICENSE-2.0 +> +> Unless required by applicable law or agreed to in writing, software +> distributed under the License is distributed on an "AS IS" BASIS, +> WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +> See the License for the specific language governing permissions and +> limitations under the License. diff --git a/docs/api-docs/CMakeLists.txt b/docs/api-docs/CMakeLists.txt index 47e17b927b..767c47b1e6 100644 --- a/docs/api-docs/CMakeLists.txt +++ b/docs/api-docs/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # Define dependencies set(depends ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::adt ${META_PROJECT_NAME}::dynlink + ${META_PROJECT_NAME}::adt ${META_PROJECT_NAME}::dynlink ${META_PROJECT_NAME}::plugin ${META_PROJECT_NAME}::detour ${META_PROJECT_NAME}::filesystem ${META_PROJECT_NAME}::reflect ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall) # TODO: add loaders, ports and examples (automatize with module names) diff --git a/docs/diagrams/threading-model-nodejs-python.drawio b/docs/diagrams/threading-model-nodejs-python.drawio new file mode 100644 index 0000000000..0ebc5bc73a --- /dev/null +++ b/docs/diagrams/threading-model-nodejs-python.drawio @@ -0,0 +1 @@ +7VjLctowFP0aL5vxAxuzDCRtFkmHKZ02rDrCUmwlwmKEANOv75Utv01JUhMymTIsdO/V85xz9bDhTJbJF4FW0R3HhBm2iRPDuTJs2xrYtqH+Jt5nHm/kZY5QUKwrlY4Z/U2009TeDcVkXasoOWeSrurOgMcxCWTNh4Tgu3q1B87qo65QSFqOWYBY2/uTYhlpr2WaZeCG0DDSQ/uuDixQ8BQKvon1eIbtPKS/LLxEeV+6/jpCmO8qLufacCaCc5mVlsmEMIVtDlvW7vOBaDFvQWL5rAY3v9jmbm16j+P5dD653yXb+0+Wk3WzRWxD8nWks5X7HKF0jUT1YhrOeBdRSWYrFKjoDjQBvkguGVgWFB8oYxPOuEjbAiLECwLwr6XgT6QSwcPRwlQd6gkQIUlycGlWARgIkfAlkWIPVXQDJxeT1uBg6Gb2rmTUyXmLKmRaQ+1EWkVh0XeJJBQ0mC8BdtABrMdg3PECCqEq/PChwvdIEITzGIxVhFtEAECyjnYd1ZjHpEGBdiFGwxjMAAAl4B8ruCkkwaUOLCnGaphOeusC6IMvu8GX3+Zr1EGXfTK2rI+YBgWGZ0yDFq7QLwLhpS0Bz+Ticf1vUPcA3MCpA+eYbeAsuwM471S42cf1SGJ8qc4/ldYMrdc0qONCEirvFYQXrrbmlchVotFNjX0ttwluHZkNJGEifCMCcoz5NuIVRLuUmPsEYUjSbX0aXSjrEaacwgQLQt3mBmM1iMqmr1tVD81GR4NRQxmjRkcSiZDIVkcp6cWyX68Dt6WDr3ADA88tRxh28j43KewSHw+6NinfXjied6Jc69ik3jbXvB5yLYaJVJJNmfM8wZRRpltqvTzfMp39ZRX+OfOtmSYD97X51khct3nknzjfhv1qwRvW1HBhWsMjikitKREUVqJuan3LZPQhZOK655WJ35LJdC8jHp9mY4Y3pX3g9ugtPLenjdlpYuqee2MedSRj9kzCdJu/kr5tFvsK7MUzqlKlXzL8gHSTsfBdJeheyHDN90aG1XUlbTxo7xCN/z9pDz4h3vZJ67ZPLjhAZtrkAvarkMeIXZfeBixlnVvOV5qfRyLlXn/GQxvJ39ejw3nmMZhVbLP57APuADdgll/0stOm/GzqXP8B \ No newline at end of file diff --git a/docs/diagrams/threading-model-nodejs-python.png b/docs/diagrams/threading-model-nodejs-python.png new file mode 100644 index 0000000000..3b77d8baff Binary files /dev/null and b/docs/diagrams/threading-model-nodejs-python.png differ diff --git a/docs/format/inspect.json b/docs/format/inspect.json new file mode 100644 index 0000000000..0f6c2d0907 --- /dev/null +++ b/docs/format/inspect.json @@ -0,0 +1,511 @@ +{ + "py": [{ + "name": "example", + "scope": { + "name": "global_namespace", + "funcs": [{ + "name": "sum", + "signature": { + "ret": { + "type": { + "name": "int", + "id": 4 + } + }, + "args": [{ + "name": "left", + "type": { + "name": "int", + "id": 4 + } + }, { + "name": "right", + "type": { + "name": "int", + "id": 4 + } + }] + } + }, { + "name": "hello", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [] + } + }, { + "name": "divide", + "signature": { + "ret": { + "type": { + "name": "float", + "id": 6 + } + }, + "args": [{ + "name": "left", + "type": { + "name": "float", + "id": 6 + } + }, { + "name": "right", + "type": { + "name": "float", + "id": 6 + } + }] + } + }, { + "name": "strcat", + "signature": { + "ret": { + "type": { + "name": "str", + "id": 7 + } + }, + "args": [{ + "name": "left", + "type": { + "name": "str", + "id": 7 + } + }, { + "name": "right", + "type": { + "name": "str", + "id": 7 + } + }] + } + }, { + "name": "multiply", + "signature": { + "ret": { + "type": { + "name": "int", + "id": 4 + } + }, + "args": [{ + "name": "left", + "type": { + "name": "int", + "id": 4 + } + }, { + "name": "right", + "type": { + "name": "int", + "id": 4 + } + }] + } + }, { + "name": "bytebuff", + "signature": { + "ret": { + "type": { + "name": "bytes", + "id": 8 + } + }, + "args": [{ + "name": "input", + "type": { + "name": "bytes", + "id": 8 + } + }] + } + }, { + "name": "dont_load_this_function", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [{ + "name": "left", + "type": { + "name": "", + "id": 15 + } + }, { + "name": "right", + "type": { + "name": "", + "id": 15 + } + }] + } + }] + } + }], + "rb": [{ + "name": "hello", + "scope": { + "name": "global_namespace", + "funcs": [{ + "name": "get_second", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [{ + "name": "first", + "type": { + "name": "Fixnum", + "id": 3 + } + }, { + "name": "second", + "type": { + "name": "Fixnum", + "id": 3 + } + }] + } + }, { + "name": "say_null", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [] + } + }, { + "name": "say_multiply", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [{ + "name": "left", + "type": { + "name": "Fixnum", + "id": 3 + } + }, { + "name": "right", + "type": { + "name": "Fixnum", + "id": 3 + } + }] + } + }, { + "name": "say_hello", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [{ + "name": "value", + "type": { + "name": "String", + "id": 7 + } + }] + } + }, { + "name": "backwardsPrime", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [{ + "name": "start", + "type": { + "name": "", + "id": 15 + } + }, { + "name": "stop", + "type": { + "name": "", + "id": 15 + } + }] + } + }] + } + }], + "cs": [{ + "name": "hello", + "scope": { + "name": "global_namespace", + "funcs": [{ + "name": "Sum", + "signature": { + "ret": { + "type": { + "name": "int", + "id": 3 + } + }, + "args": [{ + "name": "a", + "type": { + "name": "int", + "id": 3 + } + }, { + "name": "b", + "type": { + "name": "int", + "id": 3 + } + }] + } + }, { + "name": "Say", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [{ + "name": "text", + "type": { + "name": "string", + "id": 7 + } + }] + } + }, { + "name": "Concat", + "signature": { + "ret": { + "type": { + "name": "string", + "id": 7 + } + }, + "args": [{ + "name": "a", + "type": { + "name": "string", + "id": 7 + } + }, { + "name": "b", + "type": { + "name": "string", + "id": 7 + } + }] + } + }, { + "name": "SayHello", + "signature": { + "ret": { + "type": { + "name": "", + "id": 15 + } + }, + "args": [] + } + }] + } + }], + "__metacall_host__": [], + "mock": [{ + "name": "empty", + "scope": { + "name": "global_namespace", + "funcs": [{ + "name": "three_str", + "signature": { + "ret": { + "type": { + "name": "String", + "id": 7 + } + }, + "args": [{ + "name": "a_str", + "type": { + "name": "String", + "id": 7 + } + }, { + "name": "b_str", + "type": { + "name": "String", + "id": 7 + } + }, { + "name": "c_str", + "type": { + "name": "String", + "id": 7 + } + }] + } + }, { + "name": "my_empty_func_str", + "signature": { + "ret": { + "type": { + "name": "String", + "id": 7 + } + }, + "args": [] + } + }, { + "name": "my_empty_func_int", + "signature": { + "ret": { + "type": { + "name": "Integer", + "id": 3 + } + }, + "args": [] + } + }, { + "name": "new_args", + "signature": { + "ret": { + "type": { + "name": "String", + "id": 7 + } + }, + "args": [{ + "name": "a_str", + "type": { + "name": "String", + "id": 7 + } + }] + } + }, { + "name": "two_str", + "signature": { + "ret": { + "type": { + "name": "String", + "id": 7 + } + }, + "args": [{ + "name": "a_str", + "type": { + "name": "String", + "id": 7 + } + }, { + "name": "b_str", + "type": { + "name": "String", + "id": 7 + } + }] + } + }, { + "name": "two_doubles", + "signature": { + "ret": { + "type": { + "name": "Double", + "id": 6 + } + }, + "args": [{ + "name": "first_parameter", + "type": { + "name": "Double", + "id": 6 + } + }, { + "name": "second_parameter", + "type": { + "name": "Double", + "id": 6 + } + }] + } + }, { + "name": "my_empty_func", + "signature": { + "ret": { + "type": { + "name": "Integer", + "id": 3 + } + }, + "args": [] + } + }, { + "name": "mixed_args", + "signature": { + "ret": { + "type": { + "name": "Char", + "id": 1 + } + }, + "args": [{ + "name": "a_char", + "type": { + "name": "Char", + "id": 1 + } + }, { + "name": "b_int", + "type": { + "name": "Integer", + "id": 3 + } + }, { + "name": "c_long", + "type": { + "name": "Long", + "id": 4 + } + }, { + "name": "d_double", + "type": { + "name": "Double", + "id": 6 + } + }, { + "name": "e_ptr", + "type": { + "name": "Ptr", + "id": 11 + } + }] + } + }] + } + }] +} diff --git a/docs/style/source/metacall_style.tex b/docs/style/source/metacall_style.tex index b5daa789a3..aee41e857c 100644 --- a/docs/style/source/metacall_style.tex +++ b/docs/style/source/metacall_style.tex @@ -84,7 +84,6 @@ \section{Files \& Directory Tree} \dirtree{% .1 \treefolder{red}{build}. .1 \treefolder{red}{cmake}. -.1 \treefolder{red}{codegeneration}. .1 \treefolder{red}{data}. .1 \treefolder{red}{deploy}. .2 \treefolder{red}{images}. @@ -100,7 +99,6 @@ \section{Files \& Directory Tree} .3 \treefolder{red}{metacallcli}. .3 \treefolder{red}{metacallgui}. .3 \treefolder{red}{metacallnginx}. -.3 \treefolder{red}{metacallpy}. .3 \treefolder{red}{metacallweb}. .2 \treefolder{red}{filesystem}. .2 \treefolder{red}{loader}. diff --git a/githooks/unix/canonicalize_filename.sh b/githooks/unix/canonicalize_filename.sh new file mode 100644 index 0000000000..5eecabf5bc --- /dev/null +++ b/githooks/unix/canonicalize_filename.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# Provide the canonicalize filename (physical filename with out any symlinks) +# like the GNU version readlink with the -f option regardless of the version of +# readlink (GNU or BSD). + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +########################################################### +# There should be no need to change anything below this line. + +# Canonicalize by recursively following every symlink in every component of the +# specified filename. This should reproduce the results of the GNU version of +# readlink with the -f option. +# +# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac +canonicalize_filename () { + local target_file="$1" + local physical_directory="" + local result="" + + # Need to restore the working directory after work. + local working_dir="`pwd`" + + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + + # Iterate down a (possible) chain of symlinks + while [ -L "$target_file" ] + do + target_file="$(readlink -- "$target_file")" + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + done + + # Compute the canonicalized name by finding the physical path + # for the directory we're in and appending the target file. + physical_directory="`pwd -P`" + result="$physical_directory/$target_file" + + # restore the working directory after work. + cd -- "$working_dir" + + echo "$result" +} diff --git a/githooks/unix/pre-commit b/githooks/unix/pre-commit new file mode 100644 index 0000000000..5afa02a1b4 --- /dev/null +++ b/githooks/unix/pre-commit @@ -0,0 +1,50 @@ +#!/bin/sh +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# CONFIGURATION: +# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +#HOOKS="pre-commit-compile pre-commit-uncrustify" +HOOKS="pre-commit-add-contributor pre-commit-clang-format" +########################################################### +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# Absolute path to this script, e.g. /home/user/bin/foo.sh +SCRIPT="$(canonicalize_filename "$0")" + +# Absolute path this script is in, thus /home/user/bin +SCRIPTPATH="$(dirname -- "$SCRIPT")" + + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$SCRIPTPATH/$hook" ]; then + "$SCRIPTPATH/$hook" + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable." + echo "You can disable it by removing it from the list in $SCRIPT." + echo "You can skip all pre-commit hooks with --no-verify (not recommended)." + exit 1 + fi +done diff --git a/githooks/unix/pre-commit-add-contributor b/githooks/unix/pre-commit-add-contributor new file mode 100644 index 0000000000..6dbb1972dd --- /dev/null +++ b/githooks/unix/pre-commit-add-contributor @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +if git log --pretty="%ae%n%ce" | sort | uniq | grep -q "$(git config user.email)"; then + exit 0 +else + if git log --pretty="%an%n%cn" | sort | uniq | grep -q "$(git config user.name)"; then + exit 0 + else + + expected=$(printf '%s <%s>' "$(git config user.name)" "$(git config user.email)"); + + if grep -q "$expected" CONTRIBUTORS; then + exit 0 # Already in the file but first time commit in the git history + else + echo "Please add yourself (copy the next line) to the CONTRIBUTORS.md file."; + echo $expected + exit 1 + fi + + fi +fi diff --git a/githooks/unix/pre-commit-clang-format b/githooks/unix/pre-commit-clang-format new file mode 100644 index 0000000000..7c76ce8e2b --- /dev/null +++ b/githooks/unix/pre-commit-clang-format @@ -0,0 +1,256 @@ +#!/usr/bin/env bash + +# git pre-commit hook that runs a clang-format stylecheck. +# Features: +# - abort commit when commit does not comply with the style guidelines +# - create a patch of the proposed style changes +# Modifications for clang-format by rene.milk@wwu.de + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +# Some quality of life modifications made for Godot Engine. + +# Some modifs for Metacall + +################################################################## +# SETTINGS +# Set path to clang-format binary. + +# To get consistent formatting, we recommend contributors to use the same +# clang-format version as CI. +RECOMMENDED_CLANG_FORMAT_MAJOR="11" + +function find_clang_format() { + for version in 11 12 13 14 15; do + program=`which clang-format-$version 2>/dev/null` + if [ ! -z "$program" ]; then + echo "$program" + return + fi + done + + program=`which clang-format 2>/dev/null` + + if [ ! -z "$program" ]; then + version="$($program --version | rev | cut -d' ' -f1 | rev)" + major="$(echo "$version" | cut -d'.' -f1)" + + if [ "$major" -ge "$RECOMMENDED_CLANG_FORMAT_MAJOR" ]; then + echo "$program" + return + fi + fi +} + +CLANG_FORMAT=$(find_clang_format) + +# Remove any older patches from previous commits. Set to true or false. +DELETE_OLD_PATCHES=false + +# Only parse files with the extensions in FILE_EXTS. Set to true or false. +# If false every changed file in the commit will be parsed with clang-format. +# If true only files matching one of the extensions are parsed with clang-format. +PARSE_EXTS=true + +# File types to parse. Only effective when PARSE_EXTS is true. +FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx" + +# Use pygmentize instead of cat to parse diff with highlighting. +# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) +PYGMENTIZE=`which pygmentize 2>/dev/null` +if [ ! -z "$PYGMENTIZE" ]; then + READER="pygmentize -l diff" +else + READER=cat +fi + +# Path to zenity +ZENITY=`which zenity 2>/dev/null` + +# Path to xmessage +XMSG=`which xmessage 2>/dev/null` + +# Path to powershell (Windows only) +PWSH=`which powershell 2>/dev/null` + +################################################################## +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# check whether the given file matches any of the set extensions +matches_extension() { + local filename=$(basename "$1") + local extension=".${filename##*.}" + local ext + + for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done + + return 1 +} + +# necessary check for initial commit +if git rev-parse --verify HEAD >/dev/null 2>&1 ; then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +if [ ! -x "$CLANG_FORMAT" ] ; then + message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR.x.x. or superior" + + if [ ! -t 1 ] ; then + if [ -x "$ZENITY" ] ; then + $ZENITY --error --title="Error" --text="$message" + exit 1 + elif [ -x "$XMSG" ] ; then + $XMSG -center -title "Error" "$message" + exit 1 + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "$message" + exit 1 + fi + fi + printf "$message\n" + printf "Set the correct path in $(canonicalize_filename "$0").\n" + exit 1 +fi + +# create a random filename to store our generated patch +prefix="pre-commit-clang-format" +suffix="$(date +%s)" +patch="/tmp/$prefix-$suffix.patch" + +# clean up any older clang-format patches +$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch + +# create one patch containing all changes to the files +git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; +do + # ignore thirdparty folder + #if grep -q "thirdparty" <<< $file; then + # continue; + #fi + + # Only files from source folder + if grep -q "source" <<< $file; then + : # Correct + else + continue; + fi + + + # ignore file if we do check for file extensions and the file + # does not match any of the extensions specified in $FILE_EXTS + if $PARSE_EXTS && ! matches_extension "$file"; then + continue; + fi + + # clang-format our sourcefile, create a patch with diff and append it to our $patch + # The sed call is necessary to transform the patch from + # --- $file timestamp + # +++ - timestamp + # to both lines working on the same file and having a/ and b/ prefix. + # Else it can not be applied with 'git apply'. + "$CLANG_FORMAT" -style=file "$file" | \ + diff -u "$file" - | \ + sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch" +done + +# if no patch has been generated all is ok, clean up the file stub and exit +if [ ! -s "$patch" ] ; then + printf "Files in this commit comply with the clang-format rules.\n" + rm -f "$patch" + exit 0 +fi + +# a patch has been created, notify the user and exit +printf "\nThe following differences were found between the code to commit " +printf "and the clang-format rules:\n\n" + +if [ -t 1 ] ; then + $READER "$patch" + printf "\n" + # Allows us to read user input below, assigns stdin to keyboard + exec < /dev/tty + terminal="1" +else + cat "$patch" + printf "\n" + # Allows non zero zenity/powershell output + set +e + terminal="0" +fi + +while true; do + if [ $terminal = "0" ] ; then + if [ -x "$ZENITY" ] ; then + ans=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage") + if [ "$?" = "0" ] ; then + yn="Y" + else + if [ "$ans" = "Apply and stage" ] ; then + yn="S" + else + yn="N" + fi + fi + elif [ -x "$XMSG" ] ; then + $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + else + printf "Error: zenity, xmessage, or powershell executable not found.\n" + exit 1 + fi + else + read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + fi + case $yn in + [Yy] ) git apply $patch; + printf "The patch was applied. You can now stage the changes and commit again.\n\n"; + break + ;; + [Nn] ) printf "\nYou can apply these changes with:\n git apply $patch\n"; + printf "(may need to be called from the root directory of your repository)\n"; + printf "Aborting commit. Apply changes and commit again or skip checking with"; + printf " --no-verify (not recommended).\n\n"; + break + ;; + [Ss] ) git apply $patch; + git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; + do git add $file; + done + printf "The patch was applied and the changed files staged. You can now commit.\n\n"; + break + ;; + * ) echo "Please answer yes or no." + ;; + esac +done +exit 1 # we don't commit in any case diff --git a/githooks/unix/pre-push b/githooks/unix/pre-push new file mode 100644 index 0000000000..f8f287e478 --- /dev/null +++ b/githooks/unix/pre-push @@ -0,0 +1,50 @@ +#!/bin/bash +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# CONFIGURATION: +# pre-push hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +#HOOKS="pre-push-compile pre-push-uncrustify" +HOOKS="pre-push-update-version" +########################################################### +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# Absolute path to this script, e.g. /home/user/bin/foo.sh +SCRIPT="$(canonicalize_filename "$0")" + +# Absolute path this script is in, thus /home/user/bin +SCRIPTPATH="$(dirname -- "$SCRIPT")" + + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$SCRIPTPATH/$hook" ]; then + "$SCRIPTPATH/$hook" ${@:1:2} + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable." + echo "You can disable it by removing it from the list in $SCRIPT." + echo "You can skip all pre-push hooks with --no-verify (not recommended)." + exit 1 + fi +done diff --git a/githooks/unix/pre-push-update-version b/githooks/unix/pre-push-update-version new file mode 100644 index 0000000000..187e6a84f9 --- /dev/null +++ b/githooks/unix/pre-push-update-version @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Fetch all tags first +git fetch --tags $1 + +# Get the latest tag version +GIT_TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1` | cut -c2-) +FILE_VERSION=$(cat VERSION) + +# Checkout if the file version matches the last tag +if [ "${GIT_TAG_VERSION}" != "${FILE_VERSION}" ]; then + # Undo the last tag, update the version file and commit as the new tag + git tag -d v${GIT_TAG_VERSION} + printf "${GIT_TAG_VERSION}" &> VERSION + git add VERSION + git commit -m "Update version to v${GIT_TAG_VERSION}." + git tag v${GIT_TAG_VERSION} + printf "\033[0;31mYour push has been rejected in order to update VERSION.\033[0m\n" + printf "\033[0;32mPush again with:\033[0m git push $1 `git rev-parse --abbrev-ref HEAD` v${GIT_TAG_VERSION}\n" + exit 1 +fi diff --git a/.github/ISSUE_TEMPLATE.md b/githooks/win32/TODO similarity index 100% rename from .github/ISSUE_TEMPLATE.md rename to githooks/win32/TODO diff --git a/hooks/build b/hooks/build deleted file mode 100755 index 16a53e34a6..0000000000 --- a/hooks/build +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Run docker deps_node image builder -bash -c "cd tools/node && ./hooks/build" - -# Run docker deps image builder -bash -c "cd tools/base && ./hooks/build" - -# Run docker dev image builder -bash -c "cd tools/dev && ./hooks/build" - -# Run docker runtime image builder -bash -c "cd tools/core && ./hooks/build" - -# Run docker cli image builder -bash -c "cd tools/cli && ./hooks/build" diff --git a/hooks/env b/hooks/env deleted file mode 100755 index 3771488b32..0000000000 --- a/hooks/env +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load default environment variables (this must be run from tools/ path) -source ../../.env - -# These values are passed by the Hub, but if we are running locally we can get them -[ -n "$SOURCE_TYPE" ] || SOURCE_TYPE=git -[ -n "$SOURCE_BRANCH" ] || SOURCE_BRANCH=$(git symbolic-ref -q --short HEAD) -[ -n "$GIT_SHA1" ] || GIT_SHA1=$(git rev-parse -q HEAD) - -if [[ "${SOURCE_BRANCH/-*/}" =~ ^[0-9][0-9.]*$ ]]; then - VERSION=${SOURCE_BRANCH/-*/} -fi - -# Set dockerfile path if it is not absolute or if it is not defined -if [ ! -n "$DOCKERFILE_PATH" ] || [ ! "${DOCKERFILE_PATH:0:1}" = "/" ]; then - DOCKERFILE_PATH="`pwd`/Dockerfile" -fi diff --git a/hooks/pre_build b/hooks/pre_build deleted file mode 100644 index 86e08c2dce..0000000000 --- a/hooks/pre_build +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Pull docker deps_node image -docker pull registry.hub.docker.com/metacall/core:deps_node || true - -# Pull docker deps image -docker pull registry.hub.docker.com/metacall/core:deps || true - -# Pull docker dev image -docker pull registry.hub.docker.com/metacall/core:dev || true - -# Run docker runtime image builder -docker pull registry.hub.docker.com/metacall/core:runtime || true - -# Run docker cli image builder -docker pull registry.hub.docker.com/metacall/core:cli || true diff --git a/hooks/push b/hooks/push deleted file mode 100755 index b5ff9e7c56..0000000000 --- a/hooks/push +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Show message -echo "[***] Push hook running" -echo "[***] - Tagging and pushing $DOCKER_REPO:{deps_node, deps, dev, latest}" - -# Tag the deps_node image and push it -docker tag metacall/core:deps_node $DOCKER_REPO:deps_node -docker push $DOCKER_REPO:deps_node - -# Tag the deps image and push it -docker tag metacall/core:deps $DOCKER_REPO:deps -docker push $DOCKER_REPO:deps - -# Tag the dev image and push it -docker tag metacall/core:dev $DOCKER_REPO:dev -docker push $DOCKER_REPO:dev - -# Tag the runtime image and push it -docker tag metacall/core:runtime $DOCKER_REPO:runtime -docker push $DOCKER_REPO:runtime - -# Tag the cli image and push it -docker tag metacall/core:cli $DOCKER_REPO:cli -docker push $DOCKER_REPO:cli - -# Tag the latest image and push it (alias for cli) -docker tag metacall/core:cli $DOCKER_REPO:latest -docker push $DOCKER_REPO:latest diff --git a/metacall-config-version.cmake.in b/metacall-config-version.cmake.in index 36190b28ea..a25c169f21 100644 --- a/metacall-config-version.cmake.in +++ b/metacall-config-version.cmake.in @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # A library for providing a foreing function interface calls. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/metacall-config.cmake.in b/metacall-config.cmake.in index a73de8afca..eb193eed48 100644 --- a/metacall-config.cmake.in +++ b/metacall-config.cmake.in @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # A library for providing a foreing function interface calls. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 259a417c45..b7c1e402b9 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -3,7 +3,7 @@ # # Export output script directory -set(LOADER_SCRIPT_PATH "${PROJECT_BINARY_DIR}/scripts/" CACHE PATH "MetaCall scripts path") +set(LOADER_SCRIPT_PATH "${PROJECT_BINARY_DIR}/scripts" CACHE PATH "MetaCall scripts path") # Export output loader plugin directory set(LOADER_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall loaders path") @@ -18,9 +18,6 @@ set(SERIAL_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall serial lib # Export output detour plugin directory set(DETOUR_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall detour library path") -# Export output port directory -set(PORT_LIBRARY_PATH "@OUTPUT_DIRECTORY_DIR@" CACHE PATH "MetaCall port library path") - # Add extra environment varible set(EXTRA_ENVIRONMENT_VARIABLES "" CACHE PATH "MetaCall extra environment variable") @@ -54,16 +51,13 @@ set(TESTS_DETOUR_ENVIRONMENT_VARIABLES "DETOUR_LIBRARY_PATH=${DETOUR_LIBRARY_PATH}" ) -set(TESTS_PORT_ENVIRONMENT_VARIABLES - "PORT_LIBRARY_PATH=${PORT_LIBRARY_PATH}" -) - set(TESTS_ENVIRONMENT_VARIABLES ${TESTS_LOADER_ENVIRONMENT_VARIABLES} ${TESTS_CONFIGURATION_ENVIRONMENT_VARIABLES} ${TESTS_SERIAL_ENVIRONMENT_VARIABLES} ${TESTS_DETOUR_ENVIRONMENT_VARIABLES} - ${TESTS_PORT_ENVIRONMENT_VARIABLES} + ${TESTS_SANITIZER_ENVIRONMENT_VARIABLES} + ${TESTS_MEMCHECK_ENVIRONMENT_VARIABLES} ${EXTRA_ENVIRONMENT_VARIABLES} ) @@ -77,11 +71,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # Default definitions # -add_definitions( - -DCONFIGURATION_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA}/configurations/global.json\" - -DSERIAL_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" - -DLOADER_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" - -DDETOUR_LIBRARY_INSTALL_PATH=\"${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}\" +set(DEFAULT_COMPILE_DEFINITIONS + ${DEFAULT_COMPILE_DEFINITIONS} + CONFIGURATION_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA}/configurations/global.json" + SERIAL_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" + LOADER_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" + DETOUR_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" + PORT_LIBRARY_INSTALL_PATH="${CMAKE_INSTALL_PREFIX}/${INSTALL_SHARED}" ) # @@ -96,9 +92,12 @@ add_subdirectory(environment) add_subdirectory(format) add_subdirectory(log) add_subdirectory(memory) +add_subdirectory(portability) +add_subdirectory(threading) add_subdirectory(adt) add_subdirectory(filesystem) add_subdirectory(dynlink) +add_subdirectory(plugin) add_subdirectory(detour) add_subdirectory(reflect) add_subdirectory(serial) @@ -106,42 +105,50 @@ add_subdirectory(configuration) add_subdirectory(loader) add_subdirectory(metacall) -# Tests -set(IDE_FOLDER "Tests") -add_subdirectory(tests) - -# Benchmarks -set(IDE_FOLDER "Benchmarks") -add_subdirectory(benchmarks) - -# Examples -set(IDE_FOLDER "Examples") -add_subdirectory(examples) - # Loaders set(IDE_FOLDER "Loaders") add_subdirectory(loaders) -# Scripts -set(IDE_FOLDER "Scripts") -add_subdirectory(scripts) - # Serials set(IDE_FOLDER "Serials") add_subdirectory(serials) -# Serials +# Detours set(IDE_FOLDER "Detours") add_subdirectory(detours) -# Distributable -set(IDE_FOLDER "Distributable") -add_subdirectory(distributable) +# Extensions +set(IDE_FOLDER "Extensions") +add_subdirectory(extensions) + +# Extensions +set(IDE_FOLDER "Plugins") +add_subdirectory(plugins) # Ports set(IDE_FOLDER "Ports") add_subdirectory(ports) +# Scripts +set(IDE_FOLDER "Scripts") +add_subdirectory(scripts) + +# Tests +set(IDE_FOLDER "Tests") +add_subdirectory(tests) + +# Benchmarks +set(IDE_FOLDER "Benchmarks") +add_subdirectory(benchmarks) + +# CLIs +set(IDE_FOLDER "CLIs") +add_subdirectory(cli) + +# Examples +set(IDE_FOLDER "Examples") +add_subdirectory(examples) + # # Deployment # @@ -163,26 +170,22 @@ install_print("green" "LOADER_SCRIPT_PATH:") install_print("normal" "\tInstall Location: N/A") install_print("normal" "\tDefault Location: scripts") -if(BUILD_SHARED_LIBS) - install_print("green" "LOADER_LIBRARY_PATH:") - install_print("normal" "\tDescription: Directory where MetaCall loader plugins are located") - install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") - install_print("normal" "\tDefault Location: .") -endif() - install_print("green" "CONFIGURATION_PATH:") install_print("normal" "\tDescription: Path to the main global MetaCall configuration") install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_DATA}/configurations/global.json") install_print("normal" "\tDefault Location: configurations/global.json") if(BUILD_SHARED_LIBS) + install_print("green" "LOADER_LIBRARY_PATH:") + install_print("normal" "\tDescription: Directory where MetaCall loader plugins are located") + install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") + install_print("normal" "\tDefault Location: .") + install_print("green" "SERIAL_LIBRARY_PATH:") install_print("normal" "\tDescription: Directory where MetaCall serial plugins are located") install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") install_print("normal" "\tDefault Location: serials") -endif() -if(BUILD_SHARED_LIBS) install_print("green" "DETOUR_LIBRARY_PATH:") install_print("normal" "\tDescription: Directory where MetaCall detour plugins are located") install_print("normal" "\tInstall Location: ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB}") diff --git a/source/adt/CMakeLists.txt b/source/adt/CMakeLists.txt index a86fab8e8c..6083509df2 100644 --- a/source/adt/CMakeLists.txt +++ b/source/adt/CMakeLists.txt @@ -1,9 +1,3 @@ -# -# External dependencies -# - -# find_package(THIRDPARTY REQUIRED) - # # Library name and options # @@ -16,7 +10,6 @@ message(STATUS "Lib ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -44,9 +37,11 @@ set(headers ${include_path}/adt_comparable.h ${include_path}/adt_hash.h ${include_path}/adt_set.h - ${include_path}/adt_set_bucket.h + ${include_path}/adt_map.h + ${include_path}/adt_bucket.h ${include_path}/adt_trie.h ${include_path}/adt_vector.h + ${include_path}/adt_string.h ) set(sources @@ -54,7 +49,8 @@ set(sources ${source_path}/adt_comparable.c ${source_path}/adt_hash.c ${source_path}/adt_set.c - ${source_path}/adt_set_bucket.c + ${source_path}/adt_map.c + ${source_path}/adt_bucket.c ${source_path}/adt_trie.c ${source_path}/adt_vector.c ) @@ -81,30 +77,7 @@ add_library(${target} add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects -if(NOT OPTION_BUILD_DIST_LIBS) - export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -endif() - -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) # Create API export header generate_export_header(${target} @@ -150,6 +123,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log PUBLIC @@ -164,6 +138,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE + ${target_upper}_EXPORTS # Export API PUBLIC $<$>:${target_upper}_STATIC_DEFINE> @@ -189,7 +164,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC @@ -197,17 +172,3 @@ target_link_libraries(${target} INTERFACE ) - -# -# Deployment -# - -# Library -if(NOT OPTION_BUILD_DIST_LIBS) - install(TARGETS ${target} - EXPORT "${target}-export" COMPONENT dev - RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime - LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime - ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev - ) -endif() diff --git a/source/adt/include/adt/adt.h b/source/adt/include/adt/adt.h index 79f2eaf124..040bd1fc50 100644 --- a/source/adt/include/adt/adt.h +++ b/source/adt/include/adt/adt.h @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,14 +25,14 @@ #include #include -#include #include +#include #ifdef __cplusplus extern "C" { #endif -ADT_API const char * adt_print_info(void); +ADT_API const char *adt_print_info(void); #ifdef __cplusplus } diff --git a/source/adt/include/adt/adt_bucket.h b/source/adt/include/adt/adt_bucket.h new file mode 100644 index 0000000000..4fb799f189 --- /dev/null +++ b/source/adt/include/adt/adt_bucket.h @@ -0,0 +1,74 @@ +/* + * Abstract Data Type Library by Parra Studios + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * A abstract data type library providing generic containers. + * + */ + +#ifndef ADT_BUCKET_H +#define ADT_BUCKET_H 1 + +/* -- Headers -- */ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Forward Declarations -- */ + +struct pair_type; +struct bucket_type; + +/* -- Type Definitions -- */ + +typedef struct pair_type *pair; +typedef struct bucket_type *bucket; + +/* -- Member Data -- */ + +struct pair_type +{ + void *key; + void *value; +}; + +struct bucket_type +{ + size_t count; + size_t capacity; + pair pairs; +}; + +/* -- Methods -- */ + +ADT_API size_t bucket_capacity(size_t prime); + +ADT_API bucket bucket_create(size_t size); + +ADT_API int bucket_alloc_pairs(bucket b, size_t capacity); + +ADT_API int bucket_realloc_pairs(bucket b, size_t new_capacity); + +ADT_API pair bucket_get_pair(bucket b, comparable_callback compare_cb, void *key); + +ADT_API vector bucket_get_pairs_value(bucket b, comparable_callback compare_cb, void *key); + +ADT_API int bucket_insert(bucket b, void *key, void *value); + +ADT_API int bucket_remove(bucket b, comparable_callback compare_cb, void *key, void **value); + +#ifdef __cplusplus +} +#endif + +#endif /* ADT_BUCKET_H */ diff --git a/source/adt/include/adt/adt_comparable.h b/source/adt/include/adt/adt_comparable.h index 8803486394..54fc2d2b84 100644 --- a/source/adt/include/adt/adt_comparable.h +++ b/source/adt/include/adt/adt_comparable.h @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ extern "C" { #endif -typedef void * comparable; +typedef void *comparable; typedef int (*comparable_callback)(const comparable, const comparable); diff --git a/source/adt/include/adt/adt_hash.h b/source/adt/include/adt/adt_hash.h index 415fbdc176..3601d4676c 100644 --- a/source/adt/include/adt/adt_hash.h +++ b/source/adt/include/adt/adt_hash.h @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,15 @@ extern "C" { #endif +/* -- Headers -- */ + +#include + /* -- Type Definitions -- */ -typedef unsigned int hash; +typedef uintptr_t hash; -typedef void * hash_key; +typedef void *hash_key; typedef hash (*hash_callback)(const hash_key); @@ -39,6 +43,8 @@ typedef hash (*hash_callback)(const hash_key); ADT_API hash hash_callback_str(const hash_key key); +ADT_API hash hash_callback_ptr(const hash_key key); + #ifdef __cplusplus } #endif diff --git a/source/adt/include/adt/adt_map.h b/source/adt/include/adt/adt_map.h new file mode 100644 index 0000000000..2a256463ed --- /dev/null +++ b/source/adt/include/adt/adt_map.h @@ -0,0 +1,105 @@ +/* + * Abstract Data Type Library by Parra Studios + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * A abstract data type library providing generic containers. + * + */ + +#ifndef ADT_MAP_H +#define ADT_MAP_H 1 + +/* -- Headers -- */ + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Forward Declarations -- */ + +struct map_type; + +struct map_iterator_type; + +/* -- Type Definitions -- */ + +typedef struct map_type *map; + +typedef hash map_hash; + +typedef comparable map_key; + +typedef void *map_value; + +typedef void *map_cb_iterate_args; + +typedef hash_callback map_cb_hash; + +typedef comparable_callback map_cb_compare; + +typedef int (*map_cb_iterate)(map, map_key, map_value, map_cb_iterate_args); + +typedef struct map_iterator_type *map_iterator; + +/* -- Member Data -- */ + +struct map_iterator_type +{ + map m; + size_t current_bucket; + size_t current_pair; +}; + +/* -- Methods -- */ + +ADT_API map map_create(map_cb_hash hash_cb, map_cb_compare compare_cb); + +ADT_API size_t map_size(map m); + +ADT_API int map_insert(map m, map_key key, map_value value); + +ADT_API int map_insert_array(map m, map_key keys[], map_value values[], size_t size); + +ADT_API vector map_get(map m, map_key key); + +ADT_API int map_contains(map m, map_key key); + +ADT_API int map_contains_any(map dest, map src); + +ADT_API map_value map_remove(map m, map_key key); + +ADT_API vector map_remove_all(map m, map_key key); + +ADT_API void map_iterate(map m, map_cb_iterate iterate_cb, map_cb_iterate_args args); + +ADT_API int map_append(map dest, map src); + +ADT_API int map_clear(map m); + +ADT_API void map_destroy(map m); + +ADT_API void map_iterator_begin(map_iterator it, map m); + +ADT_API map_key map_iterator_key(map_iterator it); + +ADT_API map_value map_iterator_value(map_iterator it); + +ADT_API void map_iterator_next(map_iterator it); + +ADT_API int map_iterator_end(map_iterator it); + +#ifdef __cplusplus +} +#endif + +#endif /* ADT_MAP_H */ diff --git a/source/adt/include/adt/adt_set.h b/source/adt/include/adt/adt_set.h index 909530e230..a662a90b8b 100644 --- a/source/adt/include/adt/adt_set.h +++ b/source/adt/include/adt/adt_set.h @@ -1,6 +1,6 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A abstract data type library providing generic containers. * @@ -13,8 +13,8 @@ #include -#include #include +#include #ifdef __cplusplus extern "C" { @@ -32,15 +32,15 @@ struct set_iterator_type; /* -- Type Definitions -- */ -typedef struct set_type * set; +typedef struct set_type *set; typedef hash set_hash; typedef comparable set_key; -typedef void * set_value; +typedef void *set_value; -typedef void * set_cb_iterate_args; +typedef void *set_cb_iterate_args; typedef hash_callback set_cb_hash; @@ -48,7 +48,16 @@ typedef comparable_callback set_cb_compare; typedef int (*set_cb_iterate)(set, set_key, set_value, set_cb_iterate_args); -typedef struct set_iterator_type * set_iterator; +typedef struct set_iterator_type *set_iterator; + +/* -- Member Data -- */ + +struct set_iterator_type +{ + set s; + size_t current_bucket; + size_t current_pair; +}; /* -- Methods -- */ @@ -64,6 +73,10 @@ ADT_API set_value set_get(set s, set_key key); ADT_API int set_contains(set s, set_key key); +ADT_API int set_contains_any(set dest, set src); + +ADT_API int set_contains_which(set dest, set src, set_key *key); + ADT_API set_value set_remove(set s, set_key key); ADT_API void set_iterate(set s, set_cb_iterate iterate_cb, set_cb_iterate_args args); @@ -76,15 +89,15 @@ ADT_API int set_clear(set s); ADT_API void set_destroy(set s); -ADT_API set_iterator set_iterator_begin(set s); +ADT_API void set_iterator_begin(set_iterator it, set s); -ADT_API set_key set_iterator_get_key(set_iterator it); +ADT_API set_key set_iterator_key(set_iterator it); -ADT_API set_value set_iterator_get_value(set_iterator it); +ADT_API set_value set_iterator_value(set_iterator it); ADT_API void set_iterator_next(set_iterator it); -ADT_API int set_iterator_end(set_iterator * it); +ADT_API int set_iterator_end(set_iterator it); #ifdef __cplusplus } diff --git a/source/adt/include/adt/adt_set_bucket.h b/source/adt/include/adt/adt_set_bucket.h deleted file mode 100644 index 5c427c4da9..0000000000 --- a/source/adt/include/adt/adt_set_bucket.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * A abstract data type library providing generic containers. - * - */ - -#ifndef ADT_SET_BUCKET_H -#define ADT_SET_BUCKET_H 1 - -/* -- Headers -- */ - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Headers -- */ - -#include - -/* -- Forward Declarations -- */ - -struct set_pair_type; -struct set_bucket_type; - -/* -- Type Definitions -- */ - -typedef struct set_pair_type * set_pair; -typedef struct set_bucket_type * set_bucket; - -/* -- Member Data -- */ - -struct set_pair_type -{ - void * key; - void * value; -}; - -struct set_bucket_type -{ - size_t count; - size_t capacity; - set_pair pairs; -}; - -/* -- Methods -- */ - -ADT_API size_t set_bucket_capacity(size_t prime); - -ADT_API set_bucket set_bucket_create(size_t size); - -ADT_API int set_bucket_alloc_pairs(set_bucket bucket, size_t capacity); - -ADT_API int set_bucket_realloc_pairs(set_bucket bucket, size_t new_capacity); - -ADT_API set_pair set_bucket_get_pair(set_bucket bucket, comparable_callback compare_cb, void * key); - -ADT_API int set_bucket_insert(set_bucket bucket, void * key, void * value); - -ADT_API int set_bucket_remove(set_bucket bucket, comparable_callback compare_cb, void * key, void ** value); - -#ifdef __cplusplus -} -#endif - -#endif /* ADT_SET_BUCKET_H */ diff --git a/source/adt/include/adt/adt_string.h b/source/adt/include/adt/adt_string.h new file mode 100644 index 0000000000..1b50369a8d --- /dev/null +++ b/source/adt/include/adt/adt_string.h @@ -0,0 +1,50 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef ADT_STRING_H +#define ADT_STRING_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include +#include + +/* -- Macros -- */ + +/* This is a temporary solution for safe strings, it can be improved in the future */ +#define string_copy(dest, src, dest_size) \ + do \ + { \ + size_t __string_copy_length = strnlen(src, dest_size - 1); \ + memcpy(dest, src, __string_copy_length); \ + dest[__string_copy_length] = '\0'; \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* ADT_STRING_H */ diff --git a/source/adt/include/adt/adt_trie.h b/source/adt/include/adt/adt_trie.h index 36bb3f80d2..899e1f9296 100644 --- a/source/adt/include/adt/adt_trie.h +++ b/source/adt/include/adt/adt_trie.h @@ -1,6 +1,6 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A abstract data type library providing generic containers. * @@ -13,8 +13,8 @@ #include -#include #include +#include #include #ifdef __cplusplus @@ -31,15 +31,15 @@ struct trie_type; /* -- Type Definitions -- */ -typedef struct trie_type * trie; +typedef struct trie_type *trie; typedef hash trie_hash; typedef comparable trie_key; -typedef void * trie_value; +typedef void *trie_value; -typedef void * trie_cb_iterate_args; +typedef void *trie_cb_iterate_args; typedef hash_callback trie_cb_hash; diff --git a/source/adt/include/adt/adt_vector.h b/source/adt/include/adt/adt_vector.h index f8d4a172a7..d727171687 100644 --- a/source/adt/include/adt/adt_vector.h +++ b/source/adt/include/adt/adt_vector.h @@ -1,6 +1,6 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A abstract data type library providing generic containers. * @@ -27,7 +27,7 @@ struct vector_type; /* -- Type Definitions -- */ -typedef struct vector_type * vector; +typedef struct vector_type *vector; /* -- Macros -- */ @@ -74,7 +74,7 @@ typedef struct vector_type * vector; * Pointer to the first element */ #define vector_front_type(v, type_name) \ - (*((type_name*)vector_front(v))) + (*((type_name *)vector_front(v))) /** * @brief @@ -90,7 +90,7 @@ typedef struct vector_type * vector; * Pointer to the last element */ #define vector_back_type(v, type_name) \ - (*((type_name*)vector_back(v))) + (*((type_name *)vector_back(v))) /** * @brief @@ -209,12 +209,9 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_set(v, position, ¯o_vector_type_const_to_var); \ - \ } while (0) - /** * @brief * Adds a constant object at the end of vector @@ -233,9 +230,7 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_push_back(v, ¯o_vector_type_const_to_var); \ - \ } while (0) /** @@ -256,9 +251,7 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_push_front(v, ¯o_vector_type_const_to_var); \ - \ } while (0) /** @@ -284,9 +277,7 @@ typedef struct vector_type * vector; do \ { \ type_name macro_vector_type_const_to_var = constant; \ - \ vector_insert(v, position, ¯o_vector_type_const_to_var); \ - \ } while (0) /* -- Methods -- */ @@ -407,7 +398,7 @@ ADT_API size_t vector_type_size(vector v); * @return * Pointer to the first element */ -ADT_API void * vector_front(vector v); +ADT_API void *vector_front(vector v); /** * @brief @@ -419,7 +410,7 @@ ADT_API void * vector_front(vector v); * @return * Pointer to the last element */ -ADT_API void * vector_back(vector v); +ADT_API void *vector_back(vector v); /** * @brief @@ -435,7 +426,7 @@ ADT_API void * vector_back(vector v); * @return * Pointer to the element at @position */ -ADT_API void * vector_at(vector v, size_t position); +ADT_API void *vector_at(vector v, size_t position); /** * @brief @@ -451,7 +442,7 @@ ADT_API void * vector_at(vector v, size_t position); * @param[in] element * Element to be copied at @position */ -ADT_API void vector_set(vector v, size_t position, void * element); +ADT_API void vector_set(vector v, size_t position, void *element); /** * @brief @@ -475,7 +466,7 @@ ADT_API void vector_push_back_empty(vector v); * @param[in] element * Element to be inserted */ -ADT_API void vector_push_back(vector v, void * element); +ADT_API void vector_push_back(vector v, void *element); /** * @brief @@ -509,7 +500,7 @@ ADT_API void vector_push_front_empty(vector v); * @param[in] element * Element to be inserted */ -ADT_API void vector_push_front(vector v, void * element); +ADT_API void vector_push_front(vector v, void *element); /** * @brief @@ -554,7 +545,7 @@ ADT_API void vector_insert_empty(vector v, size_t position); * @param[in] element * Reference to the element to be inserted */ -ADT_API void vector_insert(vector v, size_t position, void * element); +ADT_API void vector_insert(vector v, size_t position, void *element); /** * @brief diff --git a/source/adt/source/adt.c b/source/adt/source/adt.c index 5688933d5c..eeaf243223 100644 --- a/source/adt/source/adt.c +++ b/source/adt/source/adt.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,17 +22,17 @@ #include -const char * adt_print_info() +const char *adt_print_info(void) { static const char adt_info[] = "Abstract Data Type Library " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef ADT_STATIC_DEFINE - "Compiled as static library type" - #else - "Compiled as shared library type" - #endif +#ifdef ADT_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif "\n"; diff --git a/source/adt/source/adt_bucket.c b/source/adt/source/adt_bucket.c new file mode 100644 index 0000000000..98f7398751 --- /dev/null +++ b/source/adt/source/adt_bucket.c @@ -0,0 +1,322 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +#include + +/* -- Definitions -- */ + +#define BUCKET_PAIRS_DEFAULT ((size_t)0x04) + +/* -- Methods -- */ + +size_t bucket_capacity(size_t prime) +{ + static const size_t capacity_primes[] = { + 5UL, + 11UL, + 23UL, + 47UL, + 97UL, + 199UL, + 409UL, + 823UL, + 1741UL, + 3469UL, + 6949UL, + 14033UL, + 28411UL, + 57557UL, + 116731UL, + 236897UL, + 480881UL, + 976369UL, + 1982627UL, + 4026031UL, + 8175383UL, + 16601593UL, + 33712729UL, + 68460391UL, + 139022417UL, + 282312799UL, + 573292817UL, + 1164186217UL, + 2364114217UL, + 4294967291UL, + +#if (SIZE_MAX > 0xFFFFFFFF) + (size_t)8589934583ULL, + (size_t)17179869143ULL, + (size_t)34359738337ULL, + (size_t)68719476731ULL, + (size_t)137438953447ULL, + (size_t)274877906899ULL, + (size_t)549755813881ULL, + (size_t)1099511627689ULL, + (size_t)2199023255531ULL, + (size_t)4398046511093ULL, + (size_t)8796093022151ULL, + (size_t)17592186044399ULL, + (size_t)35184372088777ULL, + (size_t)70368744177643ULL, + (size_t)140737488355213ULL, + (size_t)281474976710597ULL, + (size_t)562949953421231ULL, + (size_t)1125899906842597ULL, + (size_t)2251799813685119ULL, + (size_t)4503599627370449ULL, + (size_t)9007199254740881ULL, + (size_t)18014398509481951ULL, + (size_t)36028797018963913ULL, + (size_t)72057594037927931ULL, + (size_t)144115188075855859ULL, + (size_t)288230376151711717ULL, + (size_t)576460752303423433ULL, + (size_t)1152921504606846883ULL, + (size_t)2305843009213693951ULL, + (size_t)4611686018427387847ULL, + (size_t)9223372036854775783ULL, + (size_t)18446744073709551557ULL +#endif + }; + + static const size_t capacity_primes_size = sizeof(capacity_primes) / sizeof(capacity_primes[0]); + + if (prime < capacity_primes_size) + { + return capacity_primes[prime]; + } + + return 0; +} + +bucket bucket_create(size_t size) +{ + bucket buckets; + + size_t iterator; + + if (size == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket size: %" PRIuS, size); + return NULL; + } + + buckets = malloc(sizeof(struct bucket_type) * size); + + if (buckets == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad allocation for bucket"); + return NULL; + } + + for (iterator = 0; iterator < size; ++iterator) + { + buckets[iterator].count = 0; + buckets[iterator].capacity = 0; + buckets[iterator].pairs = NULL; + } + + return buckets; +} + +int bucket_alloc_pairs(bucket b, size_t capacity) +{ + if (b == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket"); + return 1; + } + + if (b->pairs != NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Set bucket pairs already allocated"); + return 1; + } + + b->pairs = malloc(sizeof(struct pair_type) * capacity); + + if (b->pairs == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad pairs allocation"); + return 1; + } + + b->count = 0; + b->capacity = capacity; + + return 0; +} + +int bucket_realloc_pairs(bucket b, size_t new_capacity) +{ + pair pairs = realloc(b->pairs, sizeof(struct pair_type) * new_capacity); + + if (pairs == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad pairs reallocation"); + return 1; + } + + b->pairs = pairs; + b->capacity = new_capacity; + + return 0; +} + +pair bucket_get_pair(bucket b, comparable_callback compare_cb, void *key) +{ + size_t iterator; + + if (b->pairs == NULL || b->count == 0) + { + return NULL; + } + + for (iterator = 0; iterator < b->count; ++iterator) + { + pair p = &b->pairs[iterator]; + + if (compare_cb(key, p->key) == 0) + { + return p; + } + } + + return NULL; +} + +vector bucket_get_pairs_value(bucket b, comparable_callback compare_cb, void *key) +{ + size_t iterator; + vector v; + + if (b->pairs == NULL) + { + return NULL; + } + + v = vector_create(sizeof(void *)); + + for (iterator = 0; iterator < b->count; ++iterator) + { + pair p = &b->pairs[iterator]; + + if (compare_cb(key, p->key) == 0) + { + vector_push_back(v, &p->value); + } + } + + return v; +} + +int bucket_insert(bucket b, void *key, void *value) +{ + pair p; + + if (b == NULL || key == NULL || value == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket insert parameters"); + return 1; + } + + if (b->pairs == NULL && bucket_alloc_pairs(b, BUCKET_PAIRS_DEFAULT) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket insertion pairs allocation"); + return 1; + } + + if ((b->count + 1) >= b->capacity) + { + if (bucket_realloc_pairs(b, b->capacity << 1) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket insertion pairs reallocation"); + return 1; + } + } + + p = &b->pairs[b->count]; + + p->key = key; + p->value = value; + + ++b->count; + + return 0; +} + +int bucket_remove(bucket b, comparable_callback compare_cb, void *key, void **value) +{ + size_t iterator; + + if (b == NULL || compare_cb == NULL || key == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket remove parameters"); + return 1; + } + + if (b->pairs == NULL || b->count == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket remove pairs"); + return 1; + } + + for (iterator = 0; iterator < b->count; ++iterator) + { + pair p = &b->pairs[iterator]; + + if (compare_cb(key, p->key) == 0) + { + void *deleted_value = p->value; + + size_t next = iterator + 1; + + size_t new_capacity = b->capacity >> 1; + + memmove(p, &b->pairs[next], sizeof(struct pair_type) * (b->count - next)); + + --b->count; + + if (b->count <= new_capacity && new_capacity > BUCKET_PAIRS_DEFAULT) + { + if (bucket_realloc_pairs(b, new_capacity) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid bucket remove pairs reallocation"); + return 1; + } + } + + if (value != NULL) + { + *value = deleted_value; + } + + return 0; + } + } + + return 1; +} diff --git a/source/adt/source/adt_comparable.c b/source/adt/source/adt_comparable.c index 870a483b86..037be1f9be 100644 --- a/source/adt/source/adt_comparable.c +++ b/source/adt/source/adt_comparable.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,16 +24,16 @@ int comparable_callback_str(const comparable a, const comparable b) { - const char * str_a = a; - const char * str_b = b; + const char *str_a = a; + const char *str_b = b; return strcmp(str_a, str_b); } int comparable_callback_int(const comparable a, const comparable b) { - const int * int_ptr_a = a; - const int * int_ptr_b = b; + const int *int_ptr_a = a; + const int *int_ptr_b = b; const int int_var_a = *int_ptr_a; const int int_var_b = *int_ptr_b; @@ -54,8 +54,8 @@ int comparable_callback_int(const comparable a, const comparable b) int comparable_callback_ptr(const comparable a, const comparable b) { - const void * void_ptr_a = a; - const void * void_ptr_b = b; + const void *void_ptr_a = a; + const void *void_ptr_b = b; if (void_ptr_a < void_ptr_b) { diff --git a/source/adt/source/adt_hash.c b/source/adt/source/adt_hash.c index 5c9ffd56c8..2800af3b60 100644 --- a/source/adt/source/adt_hash.c +++ b/source/adt/source/adt_hash.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,8 @@ hash hash_callback_str(const hash_key key) { - const char * str = (const char *)key; + /* djb2 */ + const char *str = (const char *)key; hash h = 0x1505; @@ -35,3 +36,25 @@ hash hash_callback_str(const hash_key key) return h; } + +hash hash_callback_ptr(const hash_key key) +{ + /* https://stackoverflow.com/a/12996028 */ + uintptr_t x = (uintptr_t)key; + +#if UINTPTR_MAX == 0xFFFFFFFF + /* 32-bit */ + x = ((x >> 16) ^ x) * 0x45D9F3B; + x = ((x >> 16) ^ x) * 0x45D9F3B; + x = (x >> 16) ^ x; +#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFF + /* 64-bit */ + x = (x ^ (x >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); + x = (x ^ (x >> 27)) * UINT64_C(0x94D049BB133111EB); + x = x ^ (x >> 31); +#else + #error "Architecture not implemented for hash function" +#endif + + return x; +} diff --git a/source/adt/source/adt_map.c b/source/adt/source/adt_map.c new file mode 100644 index 0000000000..eb1768d0d9 --- /dev/null +++ b/source/adt/source/adt_map.c @@ -0,0 +1,583 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include +#include + +#include + +/* -- Definitions -- */ + +#define MAP_BUCKET_RATIO_MIN 0.1f +#define MAP_BUCKET_RATIO_MAX 0.77f + +/* -- Member Data -- */ + +struct map_type +{ + size_t count; + size_t capacity; + size_t prime; + bucket buckets; + map_cb_hash hash_cb; + map_cb_compare compare_cb; +}; + +/* -- Methods -- */ + +map map_create(map_cb_hash hash_cb, map_cb_compare compare_cb) +{ + map m; + + if (hash_cb == NULL || compare_cb == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map creation parameters"); + return NULL; + } + + m = malloc(sizeof(struct map_type)); + + if (m == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad map allocation"); + return NULL; + } + + m->hash_cb = hash_cb; + m->compare_cb = compare_cb; + m->count = 0; + m->prime = 0; + m->capacity = bucket_capacity(m->prime); + m->buckets = bucket_create(m->capacity); + + if (m->buckets == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad map bucket creation"); + free(m); + return NULL; + } + + return m; +} + +size_t map_size(map m) +{ + if (m != NULL) + { + return m->count; + } + + return 0; +} + +static int map_bucket_rehash(map m, map new_map) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < m->capacity; ++bucket_iterator) + { + bucket b = &m->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + map_hash h = new_map->hash_cb(p->key); + + size_t index = h % new_map->capacity; + + bucket new_bucket = &new_map->buckets[index]; + + if (bucket_insert(new_bucket, p->key, p->value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket realloc insertion"); + return 1; + } + + ++new_map->count; + } + } + } + + return 0; +} + +static int map_bucket_realloc(map m) +{ + struct map_type new_map; + size_t prime = m->prime; + float ratio = (float)((float)m->count / (float)m->capacity); + + if (prime > 0 && ratio <= MAP_BUCKET_RATIO_MIN) + { + --prime; + } + else if (ratio >= MAP_BUCKET_RATIO_MAX) + { + ++prime; + } + else + { + return 0; + } + + new_map.hash_cb = m->hash_cb; + new_map.compare_cb = m->compare_cb; + new_map.count = 0; + new_map.prime = prime; + new_map.capacity = bucket_capacity(prime); + new_map.buckets = bucket_create(new_map.capacity); + + if (new_map.buckets != NULL) + { + size_t iterator; + + /* Rehash all the elements into the new map */ + map_bucket_rehash(m, &new_map); + + /* Destroy all pairs from old map */ + for (iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL) + { + free(b->pairs); + } + } + + /* Destroy all buckets from old map */ + free(m->buckets); + + m->capacity = new_map.capacity; + m->prime = new_map.prime; + m->buckets = new_map.buckets; + + return 0; + } + + return 1; +} + +int map_insert(map m, map_key key, map_value value) +{ + map_hash h; + size_t index; + bucket b; + + if (m == NULL || key == NULL || value == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map insertion parameters"); + return 1; + } + + h = m->hash_cb(key); + + index = h % m->capacity; + + b = &m->buckets[index]; + + if (bucket_insert(b, key, value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket insertion"); + return 1; + } + + ++m->count; + + return map_bucket_realloc(m); +} + +int map_insert_array(map m, map_key keys[], map_value values[], size_t size) +{ + size_t iterator; + + if (m == NULL || keys == NULL || values == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map insertion parameters"); + return 1; + } + + for (iterator = 0; iterator < size; ++iterator) + { + if (map_insert(m, keys[iterator], values[iterator]) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map array insertion"); + return 1; + } + } + + return 0; +} + +vector map_get(map m, map_key key) +{ + if (m != NULL && key != NULL) + { + map_hash h = m->hash_cb(key); + + size_t index = h % m->capacity; + + bucket b = &m->buckets[index]; + + return bucket_get_pairs_value(b, m->compare_cb, key); + } + + return NULL; +} + +int map_contains(map m, map_key key) +{ + if (m != NULL && key != NULL) + { + map_hash h = m->hash_cb(key); + + size_t index = h % m->capacity; + + bucket b = &m->buckets[index]; + + pair p = bucket_get_pair(b, m->compare_cb, key); + + if (p != NULL) + { + return 0; + } + } + + return 1; +} + +int map_contains_any(map dest, map src) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (map_contains(dest, p->key) == 0) + { + return 0; + } + } + } + } + + return 1; +} + +map_value map_remove(map m, map_key key) +{ + map_hash h; + size_t index; + bucket b; + map_value value = NULL; + + if (m == NULL || key == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map remove parameters"); + return NULL; + } + + h = m->hash_cb(key); + + index = h % m->capacity; + + b = &m->buckets[index]; + + if (bucket_remove(b, m->compare_cb, key, &value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove: %p", key); + return NULL; + } + + --m->count; + + if (map_bucket_realloc(m) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove reallocation"); + return NULL; + } + + return value; +} + +vector map_remove_all(map m, map_key key) +{ + map_hash h; + size_t index, iterator, size; + bucket b; + vector v = NULL; + + if (m == NULL || key == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map remove parameters"); + return NULL; + } + + h = m->hash_cb(key); + + index = h % m->capacity; + + b = &m->buckets[index]; + + v = map_get(m, key); + + size = vector_size(v); + + if (size == 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove: %p", key); + vector_destroy(v); + return NULL; + } + + for (iterator = 0; iterator < size; ++iterator) + { + if (bucket_remove(b, m->compare_cb, key, NULL) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove: %p", key); + vector_destroy(v); + return NULL; + } + + --m->count; + } + + if (map_bucket_realloc(m) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid map bucket remove reallocation"); + vector_destroy(v); + return NULL; + } + + return v; +} + +void map_iterate(map m, map_cb_iterate iterate_cb, map_cb_iterate_args args) +{ + if (m != NULL && m->buckets != NULL && iterate_cb != NULL) + { + size_t bucket_iterator; + + for (bucket_iterator = 0; bucket_iterator < m->capacity; ++bucket_iterator) + { + bucket b = &m->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + size_t pair_iterator; + + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (iterate_cb(m, p->key, p->value, args) != 0) + { + return; + } + } + } + } + } +} + +int map_append(map dest, map src) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (map_insert(dest, p->key, p->value) != 0) + { + return 1; + } + } + } + } + + return 0; +} + +int map_clear(map m) +{ + if (m == NULL) + { + return 1; + } + + if (m->buckets != NULL) + { + size_t iterator; + + for (iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL) + { + free(b->pairs); + } + } + + free(m->buckets); + } + + m->count = 0; + m->prime = 0; + m->capacity = bucket_capacity(m->prime); + m->buckets = bucket_create(m->capacity); + + if (m->buckets == NULL) + { + log_write("metacall", LOG_LEVEL_ERROR, "Bad map clear bucket creation"); + return 1; + } + + return 0; +} + +void map_destroy(map m) +{ + if (m == NULL) + { + return; + } + + if (m->buckets != NULL) + { + size_t iterator; + + for (iterator = 0; iterator < m->capacity; ++iterator) + { + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL) + { + free(b->pairs); + } + } + + free(m->buckets); + } + + free(m); +} + +void map_iterator_begin(map_iterator it, map m) +{ + if (it != NULL) + { + it->current_bucket = 0; + it->current_pair = 0; + + if (m != NULL && m->buckets != NULL && map_size(m) > 0) + { + it->m = m; + + map_iterator_next(it); + } + else + { + it->m = NULL; + } + } +} + +map_key map_iterator_key(map_iterator it) +{ + if (it != NULL && it->m != NULL && it->current_bucket < it->m->capacity && it->current_pair > 0) + { + return it->m->buckets[it->current_bucket].pairs[it->current_pair - 1].key; + } + + return NULL; +} + +map_value map_iterator_value(map_iterator it) +{ + if (it != NULL && it->m != NULL && it->current_bucket < it->m->capacity && it->current_pair > 0) + { + return it->m->buckets[it->current_bucket].pairs[it->current_pair - 1].value; + } + + return NULL; +} + +void map_iterator_next(map_iterator it) +{ + if (it != NULL && it->m != NULL) + { + for (; it->current_bucket < it->m->capacity; ++it->current_bucket) + { + bucket b = &it->m->buckets[it->current_bucket]; + + if (b->pairs != NULL && b->count > 0) + { + for (; it->current_pair < b->count; ++it->current_pair) + { + pair p = &b->pairs[it->current_pair]; + + if (p != NULL) + { + ++it->current_pair; + + return; + } + } + } + + it->current_pair = 0; + } + } +} + +int map_iterator_end(map_iterator it) +{ + if (it != NULL && it->m != NULL) + { + if (it->current_bucket >= it->m->capacity) + { + return 0; + } + + return 1; + } + + return 0; +} diff --git a/source/adt/source/adt_set.c b/source/adt/source/adt_set.c index 022bb70da1..2ffd457172 100644 --- a/source/adt/source/adt_set.c +++ b/source/adt/source/adt_set.c @@ -1,22 +1,34 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * * A abstract data type library providing generic containers. * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ /* -- Headers -- */ +#include #include -#include #include /* -- Definitions -- */ -#define SET_BUCKET_RATIO_MIN 0.1f -#define SET_BUCKET_RATIO_MAX 0.77f +#define SET_BUCKET_RATIO_MIN 0.1f +#define SET_BUCKET_RATIO_MAX 0.77f /* -- Member Data -- */ @@ -25,18 +37,11 @@ struct set_type size_t count; size_t capacity; size_t prime; - set_bucket buckets; + bucket buckets; set_cb_hash hash_cb; set_cb_compare compare_cb; }; -struct set_iterator_type -{ - set s; - size_t bucket; - size_t pair; -}; - /* -- Methods -- */ set set_create(set_cb_hash hash_cb, set_cb_compare compare_cb) @@ -46,7 +51,6 @@ set set_create(set_cb_hash hash_cb, set_cb_compare compare_cb) if (hash_cb == NULL || compare_cb == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set creation parameters"); - return NULL; } @@ -55,21 +59,20 @@ set set_create(set_cb_hash hash_cb, set_cb_compare compare_cb) if (s == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Bad set allocation"); + return NULL; } s->hash_cb = hash_cb; s->compare_cb = compare_cb; s->count = 0; s->prime = 0; - s->capacity = set_bucket_capacity(s->prime); - s->buckets = set_bucket_create(s->capacity); + s->capacity = bucket_capacity(s->prime); + s->buckets = bucket_create(s->capacity); if (s->buckets == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Bad set bucket creation"); - free(s); - return NULL; } @@ -86,7 +89,9 @@ size_t set_size(set s) return 0; } -int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_cb_iterate_args args) +/* + +static int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_cb_iterate_args args) { set new_set = (set)args; @@ -96,12 +101,11 @@ int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_cb_iter size_t index = h % new_set->capacity; - set_bucket bucket = &new_set->buckets[index]; + bucket b = &new_set->buckets[index]; - if (set_bucket_insert(bucket, key, value) != 0) + if (bucket_insert(b, key, value) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket realloc insertion"); - return 1; } @@ -112,13 +116,46 @@ int set_bucket_realloc_iterator(set s, set_key key, set_value value, set_cb_iter return 1; } +*/ -int set_bucket_realloc(set s) +static int set_bucket_rehash(set s, set new_set) { - struct set_type new_set; + size_t bucket_iterator, pair_iterator; - size_t prime = s->prime; + for (bucket_iterator = 0; bucket_iterator < s->capacity; ++bucket_iterator) + { + bucket b = &s->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + set_hash h = new_set->hash_cb(p->key); + + size_t index = h % new_set->capacity; + bucket new_bucket = &new_set->buckets[index]; + + if (bucket_insert(new_bucket, p->key, p->value) != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket realloc insertion"); + return 1; + } + + ++new_set->count; + } + } + } + + return 0; +} + +static int set_bucket_realloc(set s) +{ + struct set_type new_set; + size_t prime = s->prime; float ratio = (float)((float)s->count / (float)s->capacity); if (prime > 0 && ratio <= SET_BUCKET_RATIO_MIN) @@ -138,25 +175,28 @@ int set_bucket_realloc(set s) new_set.compare_cb = s->compare_cb; new_set.count = 0; new_set.prime = prime; - new_set.capacity = set_bucket_capacity(prime); - new_set.buckets = set_bucket_create(new_set.capacity); + new_set.capacity = bucket_capacity(prime); + new_set.buckets = bucket_create(new_set.capacity); if (new_set.buckets != NULL) { size_t iterator; - set_iterate(s, &set_bucket_realloc_iterator, &new_set); + /* Rehash all the elements into the new set */ + set_bucket_rehash(s, &new_set); + /* Destroy all pairs from old set */ for (iterator = 0; iterator < s->capacity; ++iterator) { - set_bucket bucket = &s->buckets[iterator]; + bucket b = &s->buckets[iterator]; - if (bucket->pairs != NULL) + if (b->pairs != NULL) { - free(bucket->pairs); + free(b->pairs); } } + /* Destroy all buckets from old set */ free(s->buckets); s->capacity = new_set.capacity; @@ -172,17 +212,13 @@ int set_bucket_realloc(set s) int set_insert(set s, set_key key, set_value value) { set_hash h; - size_t index; - - set_bucket bucket; - - set_pair pair; + bucket b; + pair p; if (s == NULL || key == NULL || value == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set insertion parameters"); - return 1; } @@ -190,21 +226,19 @@ int set_insert(set s, set_key key, set_value value) index = h % s->capacity; - bucket = &s->buckets[index]; + b = &s->buckets[index]; - pair = set_bucket_get_pair(bucket, s->compare_cb, key); + p = bucket_get_pair(b, s->compare_cb, key); - if (pair != NULL) + if (p != NULL) { - pair->value = value; - + p->value = value; return 0; } - if (set_bucket_insert(bucket, key, value) != 0) + if (bucket_insert(b, key, value) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insertion"); - return 1; } @@ -219,8 +253,7 @@ int set_insert_array(set s, set_key keys[], set_value values[], size_t size) if (s == NULL || keys == NULL || values == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set insertion parameters"); - + log_write("metacall", LOG_LEVEL_ERROR, "Invalid set array insertion parameters"); return 1; } @@ -229,7 +262,6 @@ int set_insert_array(set s, set_key keys[], set_value values[], size_t size) if (set_insert(s, keys[iterator], values[iterator]) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set array insertion"); - return 1; } } @@ -241,17 +273,17 @@ set_value set_get(set s, set_key key) { if (s != NULL && key != NULL) { - set_hash hash = s->hash_cb(key); + set_hash h = s->hash_cb(key); - size_t index = hash % s->capacity; + size_t index = h % s->capacity; - set_bucket bucket = &s->buckets[index]; + bucket b = &s->buckets[index]; - set_pair pair = set_bucket_get_pair(bucket, s->compare_cb, key); + pair p = bucket_get_pair(b, s->compare_cb, key); - if (pair != NULL) + if (p != NULL) { - return pair->value; + return p->value; } } @@ -262,15 +294,15 @@ int set_contains(set s, set_key key) { if (s != NULL && key != NULL) { - set_hash hash = s->hash_cb(key); + set_hash h = s->hash_cb(key); - size_t index = hash % s->capacity; + size_t index = h % s->capacity; - set_bucket bucket = &s->buckets[index]; + bucket b = &s->buckets[index]; - set_pair pair = set_bucket_get_pair(bucket, s->compare_cb, key); + pair p = bucket_get_pair(b, s->compare_cb, key); - if (pair != NULL) + if (p != NULL) { return 0; } @@ -279,20 +311,67 @@ int set_contains(set s, set_key key) return 1; } -set_value set_remove(set s, set_key key) +int set_contains_any(set dest, set src) { - set_hash h; + size_t bucket_iterator, pair_iterator; - size_t index; + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; + + if (set_contains(dest, p->key) == 0) + { + return 0; + } + } + } + } + + return 1; +} + +int set_contains_which(set dest, set src, set_key *key) +{ + size_t bucket_iterator, pair_iterator; + + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; - set_bucket bucket; + if (set_contains(dest, p->key) == 0) + { + *key = p->key; + return 0; + } + } + } + } + return 1; +} + +set_value set_remove(set s, set_key key) +{ + set_hash h; + size_t index; + bucket b; set_value value = NULL; if (s == NULL || key == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set remove parameters"); - return NULL; } @@ -300,12 +379,11 @@ set_value set_remove(set s, set_key key) index = h % s->capacity; - bucket = &s->buckets[index]; + b = &s->buckets[index]; - if (set_bucket_remove(bucket, s->compare_cb, key, &value) != 0) + if (bucket_remove(b, s->compare_cb, key, &value) != 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove"); - + log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove: %p", key); return NULL; } @@ -314,7 +392,6 @@ set_value set_remove(set s, set_key key) if (set_bucket_realloc(s) != 0) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove reallocation"); - return NULL; } @@ -329,17 +406,17 @@ void set_iterate(set s, set_cb_iterate iterate_cb, set_cb_iterate_args args) for (bucket_iterator = 0; bucket_iterator < s->capacity; ++bucket_iterator) { - set_bucket bucket = &s->buckets[bucket_iterator]; + bucket b = &s->buckets[bucket_iterator]; - if (bucket->pairs != NULL && bucket->count > 0) + if (b->pairs != NULL && b->count > 0) { size_t pair_iterator; - for (pair_iterator = 0; pair_iterator < bucket->count; ++pair_iterator) + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) { - set_pair pair = &bucket->pairs[pair_iterator]; + pair p = &b->pairs[pair_iterator]; - if (iterate_cb(s, pair->key, pair->value, args) != 0) + if (iterate_cb(s, p->key, p->value, args) != 0) { return; } @@ -349,40 +426,54 @@ void set_iterate(set s, set_cb_iterate iterate_cb, set_cb_iterate_args args) } } -int set_append_cb_iterate(set s, set_key key, set_value value, set_cb_iterate_args args) +int set_append(set dest, set src) { - set dest = (set)args; - - (void)s; + size_t bucket_iterator, pair_iterator; - return set_insert(dest, key, value); -} + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; -int set_append(set dest, set src) -{ - set_cb_iterate_args args = (set_cb_iterate_args)dest; + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; - set_iterate(src, &set_append_cb_iterate, args); + if (set_insert(dest, p->key, p->value) != 0) + { + return 1; + } + } + } + } return 0; } -int set_disjoint_cb_iterate(set s, set_key key, set_value value, set_cb_iterate_args args) +int set_disjoint(set dest, set src) { - set dest = (set)args; - - set_value deleted = set_remove(dest, key); + size_t bucket_iterator, pair_iterator; - (void)s; + for (bucket_iterator = 0; bucket_iterator < src->capacity; ++bucket_iterator) + { + bucket b = &src->buckets[bucket_iterator]; - return !(deleted == value); -} + if (b->pairs != NULL && b->count > 0) + { + for (pair_iterator = 0; pair_iterator < b->count; ++pair_iterator) + { + pair p = &b->pairs[pair_iterator]; -int set_disjoint(set dest, set src) -{ - set_cb_iterate_args args = (set_cb_iterate_args)dest; + set_value deleted = set_remove(dest, p->key); - set_iterate(src, &set_disjoint_cb_iterate, args); + if (deleted != p->value) + { + return 1; + } + } + } + } return 0; } @@ -400,11 +491,11 @@ int set_clear(set s) for (iterator = 0; iterator < s->capacity; ++iterator) { - set_bucket bucket = &s->buckets[iterator]; + bucket b = &s->buckets[iterator]; - if (bucket->pairs != NULL) + if (b->pairs != NULL) { - free(bucket->pairs); + free(b->pairs); } } @@ -413,13 +504,12 @@ int set_clear(set s) s->count = 0; s->prime = 0; - s->capacity = set_bucket_capacity(s->prime); - s->buckets = set_bucket_create(s->capacity); + s->capacity = bucket_capacity(s->prime); + s->buckets = bucket_create(s->capacity); if (s->buckets == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Bad set clear bucket creation"); - return 1; } @@ -439,11 +529,11 @@ void set_destroy(set s) for (iterator = 0; iterator < s->capacity; ++iterator) { - set_bucket bucket = &s->buckets[iterator]; + bucket b = &s->buckets[iterator]; - if (bucket->pairs != NULL) + if (b->pairs != NULL) { - free(bucket->pairs); + free(b->pairs); } } @@ -453,42 +543,41 @@ void set_destroy(set s) free(s); } -set_iterator set_iterator_begin(set s) +void set_iterator_begin(set_iterator it, set s) { - if (s != NULL && s->buckets != NULL && set_size(s) > 0) + if (it != NULL) { - set_iterator it = malloc(sizeof(struct set_iterator_type)); + it->current_bucket = 0; + it->current_pair = 0; - if (it != NULL) + if (s != NULL && s->buckets != NULL && set_size(s) > 0) { it->s = s; - it->bucket = 0; - it->pair = 0; set_iterator_next(it); - - return it; + } + else + { + it->s = NULL; } } - - return NULL; } -set_key set_iterator_get_key(set_iterator it) +set_key set_iterator_key(set_iterator it) { - if (it != NULL && it->bucket < it->s->capacity && it->pair > 0) + if (it != NULL && it->s != NULL && it->current_bucket < it->s->capacity && it->current_pair > 0) { - return it->s->buckets[it->bucket].pairs[it->pair - 1].key; + return it->s->buckets[it->current_bucket].pairs[it->current_pair - 1].key; } return NULL; } -set_value set_iterator_get_value(set_iterator it) +set_value set_iterator_value(set_iterator it) { - if (it != NULL && it->bucket < it->s->capacity && it->pair > 0) + if (it != NULL && it->s != NULL && it->current_bucket < it->s->capacity && it->current_pair > 0) { - return it->s->buckets[it->bucket].pairs[it->pair - 1].value; + return it->s->buckets[it->current_bucket].pairs[it->current_pair - 1].value; } return NULL; @@ -496,40 +585,38 @@ set_value set_iterator_get_value(set_iterator it) void set_iterator_next(set_iterator it) { - if (it != NULL) + if (it != NULL && it->s != NULL) { - for ( ; it->bucket < it->s->capacity; ++it->bucket) + for (; it->current_bucket < it->s->capacity; ++it->current_bucket) { - set_bucket bucket = &it->s->buckets[it->bucket]; + bucket b = &it->s->buckets[it->current_bucket]; - if (bucket->pairs != NULL && bucket->count > 0) + if (b->pairs != NULL && b->count > 0) { - for ( ; it->pair < bucket->count; ++it->pair) + for (; it->current_pair < b->count; ++it->current_pair) { - set_pair pair = &bucket->pairs[it->pair]; + pair p = &b->pairs[it->current_pair]; - if (pair != NULL) + if (p != NULL) { - ++it->pair; + ++it->current_pair; return; } } + + it->current_pair = 0; } } } } -int set_iterator_end(set_iterator * it) +int set_iterator_end(set_iterator it) { - if (it != NULL && *it != NULL) + if (it != NULL && it->s != NULL) { - if ((*it)->bucket >= (*it)->s->capacity) + if (it->current_bucket >= it->s->capacity) { - free(*it); - - *it = NULL; - return 0; } diff --git a/source/adt/source/adt_set_bucket.c b/source/adt/source/adt_set_bucket.c deleted file mode 100644 index 1bae03654a..0000000000 --- a/source/adt/source/adt_set_bucket.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * A abstract data type library providing generic containers. - * - */ - -/* -- Headers -- */ - -#include - -#include - -#include - -/* -- Definitions -- */ - -#define SET_BUCKET_PAIRS_DEFAULT ((size_t)0x04) - -/* -- Methods -- */ - -size_t set_bucket_capacity(size_t prime) -{ - static const size_t capacity_primes[] = - { - 5UL, - 11UL, - 23UL, - 47UL, - 97UL, - 199UL, - 409UL, - 823UL, - 1741UL, - 3469UL, - 6949UL, - 14033UL, - 28411UL, - 57557UL, - 116731UL, - 236897UL, - 480881UL, - 976369UL, - 1982627UL, - 4026031UL, - 8175383UL, - 16601593UL, - 33712729UL, - 68460391UL, - 139022417UL, - 282312799UL, - 573292817UL, - 1164186217UL, - 2364114217UL, - 4294967291UL, - - #if (SIZE_MAX > 0xFFFFFFFF) - (size_t)8589934583ULL, - (size_t)17179869143ULL, - (size_t)34359738337ULL, - (size_t)68719476731ULL, - (size_t)137438953447ULL, - (size_t)274877906899ULL, - (size_t)549755813881ULL, - (size_t)1099511627689ULL, - (size_t)2199023255531ULL, - (size_t)4398046511093ULL, - (size_t)8796093022151ULL, - (size_t)17592186044399ULL, - (size_t)35184372088777ULL, - (size_t)70368744177643ULL, - (size_t)140737488355213ULL, - (size_t)281474976710597ULL, - (size_t)562949953421231ULL, - (size_t)1125899906842597ULL, - (size_t)2251799813685119ULL, - (size_t)4503599627370449ULL, - (size_t)9007199254740881ULL, - (size_t)18014398509481951ULL, - (size_t)36028797018963913ULL, - (size_t)72057594037927931ULL, - (size_t)144115188075855859ULL, - (size_t)288230376151711717ULL, - (size_t)576460752303423433ULL, - (size_t)1152921504606846883ULL, - (size_t)2305843009213693951ULL, - (size_t)4611686018427387847ULL, - (size_t)9223372036854775783ULL, - (size_t)18446744073709551557ULL - #endif - }; - - static const size_t capacity_primes_size = sizeof(capacity_primes) / sizeof(capacity_primes[0]); - - if (prime < capacity_primes_size) - { - return capacity_primes[prime]; - } - - return 0; -} - -set_bucket set_bucket_create(size_t size) -{ - set_bucket buckets; - - size_t iterator; - - if (size == 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket size: %" PRIuS, size); - - return NULL; - } - - buckets = malloc(sizeof(struct set_bucket_type) * size); - - if (buckets == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Bad allocation for set bucket"); - - return NULL; - } - - for (iterator = 0; iterator < size; ++iterator) - { - buckets[iterator].count = 0; - buckets[iterator].capacity = 0; - buckets[iterator].pairs = NULL; - } - - return buckets; -} - -int set_bucket_alloc_pairs(set_bucket bucket, size_t capacity) -{ - if (bucket == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket"); - - return 1; - } - - if (bucket->pairs != NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Set bucket pairs already allocated"); - - return 1; - } - - bucket->pairs = malloc(sizeof(struct set_pair_type) * capacity); - - if (bucket->pairs == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Bad set pairs allocation"); - - return 1; - } - - bucket->count = 0; - bucket->capacity = capacity; - - return 0; -} - -int set_bucket_realloc_pairs(set_bucket bucket, size_t new_capacity) -{ - set_pair pairs = realloc(bucket->pairs, sizeof(struct set_pair_type) * new_capacity); - - if (pairs == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Bad set pairs reallocation"); - - return 1; - } - - bucket->pairs = pairs; - bucket->capacity = new_capacity; - - return 0; -} - -set_pair set_bucket_get_pair(set_bucket bucket, comparable_callback compare_cb, void * key) -{ - size_t iterator; - - if (bucket->pairs == NULL || bucket->count == 0) - { - return NULL; - } - - for (iterator = 0; iterator < bucket->count; ++iterator) - { - set_pair pair = &bucket->pairs[iterator]; - - if (compare_cb(key, pair->key) == 0) - { - return pair; - } - } - - return NULL; -} - -int set_bucket_insert(set_bucket bucket, void * key, void * value) -{ - set_pair pair; - - if (bucket == NULL || key == NULL || value == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insert parameters"); - - return 1; - } - - if (bucket->pairs == NULL && set_bucket_alloc_pairs(bucket, SET_BUCKET_PAIRS_DEFAULT) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insertion pairs allocation"); - - return 1; - } - - if ((bucket->count + 1) >= bucket->capacity) - { - if (set_bucket_realloc_pairs(bucket, bucket->capacity << 1) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket insertion pairs reallocation"); - - return 1; - } - } - - pair = &bucket->pairs[bucket->count]; - - pair->key = key; - pair->value = value; - - ++bucket->count; - - return 0; -} - -int set_bucket_remove(set_bucket bucket, comparable_callback compare_cb, void * key, void ** value) -{ - size_t iterator; - - if (bucket == NULL || compare_cb == NULL || key == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove parameters"); - - return 1; - } - - if (bucket->pairs == NULL || bucket->count == 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove pairs"); - - return 1; - } - - for (iterator = 0; iterator < bucket->count; ++iterator) - { - set_pair pair = &bucket->pairs[iterator]; - - if (compare_cb(key, pair->key) == 0) - { - void * deleted_value = pair->value; - - size_t next = iterator + 1; - - size_t new_capacity = bucket->capacity >> 1; - - memmove(pair, &bucket->pairs[next], sizeof(struct set_pair_type) * (bucket->count - next)); - - --bucket->count; - - if (bucket->count <= new_capacity && new_capacity > SET_BUCKET_PAIRS_DEFAULT) - { - if (set_bucket_realloc_pairs(bucket, new_capacity) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid set bucket remove pairs reallocation"); - - return 1; - } - } - - *value = deleted_value; - - return 0; - } - } - - return 1; -} diff --git a/source/adt/source/adt_trie.c b/source/adt/source/adt_trie.c index 36d039842c..047c20a698 100644 --- a/source/adt/source/adt_trie.c +++ b/source/adt/source/adt_trie.c @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,14 @@ /* -- Headers -- */ -#include #include +#include #include /* -- Definitions -- */ -#define TRIE_CAPACITY_MIN ((size_t)0x10) +#define TRIE_CAPACITY_MIN ((size_t)0x10) /* -- Forward Declarations -- */ @@ -45,84 +45,92 @@ struct trie_node_suffixes_iterator_args_type; /* -- Type Definitions -- */ -typedef struct trie_node_ref_type * trie_node_ref; +typedef struct trie_node_ref_type *trie_node_ref; -typedef struct trie_node_free_type * trie_node_free; +typedef struct trie_node_free_type *trie_node_free; -typedef struct trie_node_type * trie_node; +typedef struct trie_node_type *trie_node; -typedef struct trie_node_set_iterator_args_type * trie_node_set_iterator_args; +typedef struct trie_node_set_iterator_args_type *trie_node_set_iterator_args; -typedef struct trie_node_append_iterator_args_type * trie_node_append_iterator_args; +typedef struct trie_node_append_iterator_args_type *trie_node_append_iterator_args; -typedef struct trie_node_suffixes_iterator_args_type * trie_node_suffixes_iterator_args; +typedef struct trie_node_suffixes_iterator_args_type *trie_node_suffixes_iterator_args; /* -- Member Data -- */ struct trie_node_ref_type { - trie_key key; /**< Pointer to the key of the node */ - size_t index; /**< Reference to node in trie set */ + trie_key key; /**< Pointer to the key of the node */ + size_t index; /**< Reference to node in trie set */ }; struct trie_node_free_type { - trie_node_free next; /**< Reference to the next node in the list */ - size_t index; /**< Reference to node in trie set */ + trie_node_free next; /**< Reference to the next node in the list */ + size_t index; /**< Reference to node in trie set */ }; struct trie_node_type { - size_t parent_index; /**< Reference to parent trie node */ - size_t self_index; /**< Reference to itself inside trie node list */ - trie_key key; /**< Pointer to key of the node */ - trie_value value; /**< Pointer to data of the node */ - set childs; /**< Set with references to child trie nodes (trie_key -> trie_node_ref) */ + size_t parent_index; /**< Reference to parent trie node */ + size_t self_index; /**< Reference to itself inside trie node list */ + trie_key key; /**< Pointer to key of the node */ + trie_value value; /**< Pointer to data of the node */ + set childs; /**< Set with references to child trie nodes (trie_key -> trie_node_ref) */ }; struct trie_type { - trie_node root; /**< Trie root node */ - trie_node node_list; /**< Array of trie nodes */ - size_t size; /**< Size of current nodes inside node list */ - size_t capacity; /**< Size of allocated nodes in memory */ - trie_node_free free_node_list; /**< List of free nodes in array (size_t) */ - size_t key_limit; /**< Maximum number of childs per trie node (0 == disabled) */ - size_t depth_limit; /**< Maximum number of depth levels in a trie (0 == disabled) */ - trie_cb_hash hash_cb; /**< Hash callback for node insertion */ - trie_cb_compare compare_cb; /**< Compare callback for value comparison */ + trie_node root; /**< Trie root node */ + trie_node node_list; /**< Array of trie nodes */ + size_t size; /**< Size of current nodes inside node list */ + size_t capacity; /**< Size of allocated nodes in memory */ + trie_node_free free_node_list; /**< List of free nodes in array (size_t) */ + size_t key_limit; /**< Maximum number of childs per trie node (0 == disabled) */ + size_t depth_limit; /**< Maximum number of depth levels in a trie (0 == disabled) */ + trie_cb_hash hash_cb; /**< Hash callback for node insertion */ + trie_cb_compare compare_cb; /**< Compare callback for value comparison */ }; struct trie_node_set_iterator_args_type { - trie t; /**< Pointer to trie */ - trie_cb_iterate iterate_cb; /**< Pointer to iterator callback */ + trie t; /**< Pointer to trie */ + trie_cb_iterate iterate_cb; /**< Pointer to iterator callback */ trie_cb_iterate_args args; /**< Pointer to iterator arguments */ }; struct trie_node_append_iterator_args_type { - trie dest; /**< Pointer to destination trie */ - vector prefixes; /**< Vector containing prefixes for each iteration */ + trie dest; /**< Pointer to destination trie */ + vector prefixes; /**< Vector containing prefixes for each iteration */ }; struct trie_node_suffixes_iterator_args_type { - trie suffix_trie; /**< Pointer to new suffix trie */ - vector prefixes; /**< Vector containing prefixes for each iteration */ + trie suffix_trie; /**< Pointer to new suffix trie */ + vector prefixes; /**< Vector containing prefixes for each iteration */ }; /* -- Private Methods -- */ -trie_node trie_node_get(trie t, vector keys); +static trie_node trie_node_get(trie t, vector keys); + +static trie_node trie_node_insert(trie t, trie_node parent, trie_key key, trie_value value); + +static int trie_node_childs_cb_iterator(set s, set_key key, set_value value, set_cb_iterate_args args); -trie_node trie_node_insert(trie t, trie_node parent, trie_key key, trie_value value); +static void trie_node_iterate_recursive(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_iterate_args args); -void trie_node_iterate_recursive(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_iterate_args args); +static void trie_node_iterate(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_iterate_args args); -void trie_node_iterate(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_iterate_args args); +static int trie_node_append_cb_iterator(trie t, trie_key key, trie_value value, trie_cb_iterate_args args); -int trie_node_clear(trie t, trie_node n); +static int trie_node_clear(trie t, trie_node n); + +static trie_node trie_node_find(trie t, trie_key key); + +static int trie_node_suffixes_cb_iterator(trie t, trie_key key, trie_value value, trie_cb_iterate_args args); /* -- Methods -- */ @@ -264,7 +272,7 @@ trie_node trie_node_insert(trie t, trie_node parent, trie_key key, trie_value va { if ((t->size + 1) >= t->capacity) { - register void * node_list; + register void *node_list; size_t capacity = t->capacity << 1; @@ -341,7 +349,7 @@ int trie_insert(trie t, vector keys, trie_value value) for (iterator = 0; current_node != NULL && iterator < size; ++iterator) { - trie_key * key_ptr = vector_at(keys, iterator); + trie_key *key_ptr = vector_at(keys, iterator); trie_node next_node = NULL; @@ -393,7 +401,7 @@ trie_node trie_node_get(trie t, vector keys) for (iterator = 0; current_node != NULL && iterator < size; ++iterator) { - trie_key * key_ptr = vector_at(keys, iterator); + trie_key *key_ptr = vector_at(keys, iterator); trie_node next_node = NULL; @@ -506,7 +514,7 @@ void trie_node_iterate(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_ while (vector_size(node_stack) > 0) { - trie_node * back_ptr = vector_back(node_stack); + trie_node *back_ptr = vector_back(node_stack); vector_pop_back(node_stack); @@ -516,10 +524,11 @@ void trie_node_iterate(trie t, trie_node n, trie_cb_iterate iterate_cb, trie_cb_ if (back->childs != NULL) { - set_iterator it; - for (it = set_iterator_begin(back->childs); set_iterator_end(&it) > 0; set_iterator_next(it)) + struct set_iterator_type it; + + for (set_iterator_begin(&it, back->childs); set_iterator_end(&it) > 0; set_iterator_next(&it)) { - trie_node_ref ref_node = set_iterator_get_value(it); + trie_node_ref ref_node = set_iterator_value(&it); trie_node current_node = &t->node_list[ref_node->index]; @@ -589,7 +598,7 @@ int trie_node_clear(trie t, trie_node n) while (vector_size(node_stack) > 0) { - trie_node * back_ptr = vector_back(node_stack); + trie_node *back_ptr = vector_back(node_stack); vector_pop_back(node_stack); @@ -601,11 +610,11 @@ int trie_node_clear(trie t, trie_node n) if (back->childs != NULL) { - set_iterator it; + struct set_iterator_type it; - for (it = set_iterator_begin(back->childs); set_iterator_end(&it) > 0; set_iterator_next(it)) + for (set_iterator_begin(&it, back->childs); set_iterator_end(&it) > 0; set_iterator_next(&it)) { - trie_node_ref ref_node = set_iterator_get_value(it); + trie_node_ref ref_node = set_iterator_value(&it); trie_node current_node = &t->node_list[ref_node->index]; @@ -666,21 +675,20 @@ trie_node trie_node_find(trie t, trie_key key) while (vector_size(node_stack) > 0) { - trie_node * back_ptr = vector_back(node_stack); + trie_node *back_ptr = vector_back(node_stack); vector_pop_back(node_stack); if (back_ptr != NULL && *back_ptr != NULL) { trie_node back = *back_ptr; - - set_iterator it = NULL; + struct set_iterator_type it; if (back->childs != NULL) { - for (it = set_iterator_begin(back->childs); set_iterator_end(&it) > 0; set_iterator_next(it)) + for (set_iterator_begin(&it, back->childs); set_iterator_end(&it) > 0; set_iterator_next(&it)) { - trie_node_ref ref_node = set_iterator_get_value(it); + trie_node_ref ref_node = set_iterator_value(&it); trie_node current_node = &t->node_list[ref_node->index]; @@ -690,9 +698,10 @@ trie_node trie_node_find(trie t, trie_key key) if (back->key != NULL && t->compare_cb(back->key, key) == 0) { + /* TODO: it may be un-initialized here */ while (set_iterator_end(&it) > 0) { - set_iterator_next(it); + set_iterator_next(&it); } vector_destroy(node_stack); diff --git a/source/adt/source/adt_vector.c b/source/adt/source/adt_vector.c index 7137d9d1fe..0047cc1185 100644 --- a/source/adt/source/adt_vector.c +++ b/source/adt/source/adt_vector.c @@ -1,9 +1,21 @@ /* * Abstract Data Type Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * * A abstract data type library providing generic containers. * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * */ /* -- Headers -- */ @@ -16,18 +28,18 @@ /* -- Definitions -- */ -#define VECTOR_CAPACITY_INCREMENT 2 /**< Capacity increment */ -#define VECTOR_CAPACITY_MIN 16 /**< Minimum capacity */ -#define VECTOR_CAPACITY_MIN_USED 8 /**< Max unused capacity since free memory */ +#define VECTOR_CAPACITY_INCREMENT 2 /**< Capacity increment */ +#define VECTOR_CAPACITY_MIN 16 /**< Minimum capacity */ +#define VECTOR_CAPACITY_MIN_USED 8 /**< Max unused capacity since free memory */ /* -- Member Data -- */ struct vector_type { - size_t type_size; /**< Size of element type */ - size_t capacity; /**< Allocated capacity of the vector */ - size_t size; /**< Amount of actual elements cointained in vector */ - void * data; /**< Pointer to memory block */ + size_t type_size; /**< Size of element type */ + size_t capacity; /**< Allocated capacity of the vector */ + size_t size; /**< Amount of actual elements cointained in vector */ + void *data; /**< Pointer to memory block */ }; /* -- Private Methods -- */ @@ -45,15 +57,15 @@ struct vector_type * @return * A pointer to data vector memory block with offset @bytes */ -void * vector_data_offset_bytes(vector v, size_t bytes); +static void *vector_data_offset_bytes(vector v, size_t bytes); /* -- Methods -- */ -void * vector_data_offset_bytes(vector v, size_t bytes) +void *vector_data_offset_bytes(vector v, size_t bytes) { if (v != NULL && v->data != NULL && bytes < v->capacity * v->type_size) { - return ((void*)(((char*)v->data) + (bytes))); + return ((void *)(((char *)v->data) + (bytes))); } return NULL; @@ -139,7 +151,7 @@ int vector_reserve(vector v, size_t capacity) { if (v != NULL && capacity != v->capacity) { - register void * data; + register void *data; if (capacity < VECTOR_CAPACITY_MIN) { @@ -227,7 +239,7 @@ size_t vector_type_size(vector v) return 0; } -void * vector_front(vector v) +void *vector_front(vector v) { if (v != NULL) { @@ -237,7 +249,7 @@ void * vector_front(vector v) return NULL; } -void * vector_back(vector v) +void *vector_back(vector v) { if (v != NULL) { @@ -247,7 +259,7 @@ void * vector_back(vector v) return NULL; } -void * vector_at(vector v, size_t position) +void *vector_at(vector v, size_t position) { if (v != NULL) { @@ -257,7 +269,7 @@ void * vector_at(vector v, size_t position) return NULL; } -void vector_set(vector v, size_t position, void * element) +void vector_set(vector v, size_t position, void *element) { if (v != NULL && position < v->capacity && element != NULL) { @@ -283,7 +295,7 @@ void vector_push_back_empty(vector v) } } -void vector_push_back(vector v, void * element) +void vector_push_back(vector v, void *element) { if (v != NULL) { @@ -337,7 +349,7 @@ void vector_push_front_empty(vector v) } } -void vector_push_front(vector v, void * element) +void vector_push_front(vector v, void *element) { if (v != NULL && element != NULL) { @@ -398,7 +410,7 @@ void vector_insert_empty(vector v, size_t position) } } -void vector_insert(vector v, size_t position, void * element) +void vector_insert(vector v, size_t position, void *element) { if (v != NULL) { diff --git a/source/benchmarks/CMakeLists.txt b/source/benchmarks/CMakeLists.txt index 65160a2cd4..107a348e81 100644 --- a/source/benchmarks/CMakeLists.txt +++ b/source/benchmarks/CMakeLists.txt @@ -18,7 +18,7 @@ if(NOT GBENCH_FOUND) include(InstallGBench) if(NOT GBENCH_FOUND) - message(STATUS "GBench libraries not found") + message(SEND_ERROR "GBench libraries not found") return() endif() @@ -49,12 +49,20 @@ if(GBENCH_INSTALL) add_dependencies(GBench google-bench-depends) endif() +# Create output directory +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/benchmarks) + # # Benchmarks # include(CTest) +add_subdirectory(set_bench) +add_subdirectory(log_bench) add_subdirectory(metacall_py_c_api_bench) add_subdirectory(metacall_py_call_bench) add_subdirectory(metacall_py_init_bench) +add_subdirectory(metacall_node_call_bench) +add_subdirectory(metacall_rb_call_bench) +add_subdirectory(metacall_cs_call_bench) diff --git a/source/benchmarks/log_bench/CMakeLists.txt b/source/benchmarks/log_bench/CMakeLists.txt new file mode 100644 index 0000000000..f1ba5575ea --- /dev/null +++ b/source/benchmarks/log_bench/CMakeLists.txt @@ -0,0 +1,146 @@ +# +# Executable name and options +# + +# Target name +set(target log-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/log_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + log +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/log_bench/source/log_bench.cpp b/source/benchmarks/log_bench/source/log_bench.cpp new file mode 100644 index 0000000000..75bdb547bd --- /dev/null +++ b/source/benchmarks/log_bench/source/log_bench.cpp @@ -0,0 +1,101 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +static int stream_write(void *, const char *, const size_t) +{ + // Disable stream write so we do not count stdout on the benchmark + return 0; +} + +static int stream_flush(void *) +{ + // Disable stream flush so we do not count stdout on the benchmark + return 0; +} + +class log_bench : public benchmark::Fixture +{ +public: + void SetUp(benchmark::State &state) + { + if (log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_custom(NULL, &stream_write, &stream_flush)) != 0) + { + state.SkipWithError("Error creating the log"); + } + } + + void TearDown(benchmark::State &) + { + } +}; + +BENCHMARK_DEFINE_F(log_bench, call_macro) +(benchmark::State &state) +{ + const int64_t call_count = 10000; + + for (auto _ : state) + { + for (int64_t it = 0; it < call_count; ++it) + { + log_write("metacall", LOG_LEVEL_ERROR, "Message"); + } + } + + state.SetLabel("Log Benchmark - Call Macro"); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(log_bench, call_macro) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +BENCHMARK_DEFINE_F(log_bench, call_va) +(benchmark::State &state) +{ + const int64_t call_count = 10000; + + for (auto _ : state) + { + for (int64_t it = 0; it < call_count; ++it) + { + log_write_impl_va("metacall", LOG_PREPROCESSOR_LINE, log_record_function(), __FILE__, LOG_LEVEL_ERROR, "Message"); + } + } + + state.SetLabel("Log Benchmark - Call Variadic"); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(log_bench, call_va) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +BENCHMARK_MAIN(); diff --git a/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt b/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt new file mode 100644 index 0000000000..80db3ce393 --- /dev/null +++ b/source/benchmarks/metacall_cs_call_bench/CMakeLists.txt @@ -0,0 +1,158 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cs-call-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/metacall_cs_call_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with sanitizers: + # Tracer caught signal 11: addr=0x5000002b0 pc=0x7f614774a0f0 sp=0x7f609fb40d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp b/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp new file mode 100644 index 0000000000..179dfaf6c0 --- /dev/null +++ b/source/benchmarks/metacall_cs_call_bench/source/metacall_cs_call_bench.cpp @@ -0,0 +1,192 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_cs_call_bench : public benchmark::Fixture +{ +public: +}; + +BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_va_args) +(benchmark::State &state) +{ + const int64_t call_count = 500000; + const int64_t call_size = sizeof(int) * 3; // (int, int) -> int + + for (auto _ : state) + { +/* CSharp */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacall("sum", 0, 0); + + state.PauseTiming(); + + if (ret == NULL) + { + state.SkipWithError("Null return value from sum"); + } + + if (metacall_value_to_int(ret) != 0) + { + state.SkipWithError("Invalid return value from sum"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + } +#endif /* OPTION_BUILD_LOADERS_CS */ + } + + state.SetLabel("MetaCall CSharp Call Benchmark - Variadic Argument Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_cs_call_bench, call_va_args) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(5); + +BENCHMARK_DEFINE_F(metacall_cs_call_bench, call_array_args) +(benchmark::State &state) +{ + const int64_t call_count = 500000; + const int64_t call_size = sizeof(int) * 3; // (int, int) -> int + + for (auto _ : state) + { +/* CSharp */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + state.PauseTiming(); + + void *args[2] = { + metacall_value_create_int(0), + metacall_value_create_int(0) + }; + + state.ResumeTiming(); + + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacallv("sum", args); + + state.PauseTiming(); + + if (ret == NULL) + { + state.SkipWithError("Null return value from sum"); + } + + if (metacall_value_to_int(ret) != 0) + { + state.SkipWithError("Invalid return value from sum"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + + state.PauseTiming(); + + for (auto arg : args) + { + metacall_value_destroy(arg); + } + + state.ResumeTiming(); + } +#endif /* OPTION_BUILD_LOADERS_CS */ + } + + state.SetLabel("MetaCall CSharp Call Benchmark - Array Argument Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_cs_call_bench, call_array_args) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(5); + +/* TODO: NetCore re-initialization */ +/* BENCHMARK_MAIN(); */ + +int main(int argc, char **argv) +{ + ::benchmark::Initialize(&argc, argv); + + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) + { + return 1; + } + + /* TODO: MetaCall NetCore Loader does not work with re-initalization */ + /* Maybe the bug cannot be solved, but if it is eventually solved, */ + /* use SetUp and TearDown in the Fixture instead of this */ + + metacall_print_info(); + + metacall_log_null(); + + if (metacall_initialize() != 0) + { + return 1; + } + +/* CSharp */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + static const char tag[] = "cs"; + + static const char sum[] = + "using System;\n" + "namespace Scripts {\n" + "\tpublic class Program {\n" + "\t\tpublic static int sum(int a, int b) {\n" + "\t\t\treturn 0;\n" + "\t\t}\n" + "\t}\n" + "}\n"; + + if (metacall_load_from_memory(tag, sum, sizeof(sum), NULL) != 0) + { + metacall_destroy(); + return 1; + } + } +#endif /* OPTION_BUILD_LOADERS_CS */ + + ::benchmark::RunSpecifiedBenchmarks(); + + metacall_destroy(); + + return 0; +} diff --git a/source/benchmarks/metacall_node_call_bench/CMakeLists.txt b/source/benchmarks/metacall_node_call_bench/CMakeLists.txt new file mode 100644 index 0000000000..16ae8e7684 --- /dev/null +++ b/source/benchmarks/metacall_node_call_bench/CMakeLists.txt @@ -0,0 +1,147 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-call-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/metacall_node_call_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp b/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp new file mode 100644 index 0000000000..16d2c987b5 --- /dev/null +++ b/source/benchmarks/metacall_node_call_bench/source/metacall_node_call_bench.cpp @@ -0,0 +1,298 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_call_bench : public benchmark::Fixture +{ +public: +}; + +BENCHMARK_DEFINE_F(metacall_node_call_bench, call_va_args) +(benchmark::State &state) +{ + const int64_t call_count = 100000; + const int64_t call_size = sizeof(double) * 3; // (double, double) -> double + const enum metacall_value_id int_mem_type_ids[] = { + METACALL_DOUBLE, METACALL_DOUBLE + }; + + // Print memory usage + // metacall_value_destroy(metacall("mem_check")); + + for (auto _ : state) + { +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacallt("int_mem_type", int_mem_type_ids, 0.0, 0.0); + + state.PauseTiming(); + + if (ret == NULL) + { + state.SkipWithError("Null return value from int_mem_type"); + } + + if (metacall_value_to_double(ret) != 0.0) + { + state.SkipWithError("Invalid return value from int_mem_type"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + } + + state.SetLabel("MetaCall NodeJS Call Benchmark - Variadic Argument Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_node_call_bench, call_va_args) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +BENCHMARK_DEFINE_F(metacall_node_call_bench, call_array_args) +(benchmark::State &state) +{ + const int64_t call_count = 100000; + const int64_t call_size = sizeof(double) * 3; // (double, double) -> double + + // Print memory usage + // metacall_value_destroy(metacall("mem_check")); + + for (auto _ : state) + { +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + state.PauseTiming(); + + void *args[2] = { + metacall_value_create_double(0.0), + metacall_value_create_double(0.0) + }; + + state.ResumeTiming(); + + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacallv("int_mem_type", args); + + state.PauseTiming(); + + if (ret == NULL) + { + state.SkipWithError("Null return value from int_mem_type"); + } + + if (metacall_value_to_double(ret) != 0.0) + { + state.SkipWithError("Invalid return value from int_mem_type"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + + state.PauseTiming(); + + for (auto arg : args) + { + metacall_value_destroy(arg); + } + + state.ResumeTiming(); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + } + + state.SetLabel("MetaCall NodeJS Call Benchmark - Array Argument Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_node_call_bench, call_array_args) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +BENCHMARK_DEFINE_F(metacall_node_call_bench, call_async) +(benchmark::State &state) +{ + const int64_t call_count = 100000; + const int64_t call_size = sizeof(double) * 3; // (double, double) -> double + + // Print memory usage + // metacall_value_destroy(metacall("mem_check")); + + for (auto _ : state) + { +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + state.PauseTiming(); + + void *args[2] = { + metacall_value_create_double(0.0), + metacall_value_create_double(0.0) + }; + + state.ResumeTiming(); + + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacall_await( + "int_mem_async_type", args, [](void *result, void *data) -> void * { + benchmark::State *state = static_cast(data); + + if (metacall_value_to_double(result) != 0.0) + { + state->SkipWithError("Invalid return value from int_mem_async_type"); + } + + state->PauseTiming(); + + return NULL; + }, + NULL, static_cast(&state)); + + if (ret == NULL) + { + state.SkipWithError("Null return value from int_mem_async_type"); + } + + if (metacall_value_id(ret) != METACALL_FUTURE) + { + state.SkipWithError("Invalid return type from int_mem_async_type"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + + state.PauseTiming(); + + for (auto arg : args) + { + metacall_value_destroy(arg); + } + + state.ResumeTiming(); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + } + + state.SetLabel("MetaCall NodeJS Call Benchmark - Async Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_node_call_bench, call_async) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(3); + +/* TODO: NodeJS re-initialization */ +/* BENCHMARK_MAIN(); */ + +int main(int argc, char **argv) +{ + ::benchmark::Initialize(&argc, argv); + + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) + { + return 1; + } + + /* TODO: MetaCall NodeJS Loader does not work with re-initalization */ + /* Maybe the bug cannot be solved, but if it is eventually solved, */ + /* use SetUp and TearDown in the Fixture instead of this */ + + metacall_print_info(); + + metacall_log_null(); + + if (metacall_initialize() != 0) + { + return 1; + } + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char tag[] = "node"; + + static const char int_mem_type[] = + "#!/usr/bin/env node\n" + "function mem_check() {\n" + " const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;\n" + " const memoryData = process.memoryUsage();\n" + " const memoryUsage = {\n" + " rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`,\n" + " heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`,\n" + " heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`,\n" + " external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`,\n" + " };\n" + " console.log(memoryUsage);\n" + "}\n" + "module.exports = {\n" + " mem_check,\n" + " int_mem_type: (left, right) => 0,\n" + " int_mem_async_type: async (left, right) => new Promise(resolve => 0),\n" + "};\n"; + + if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) + { + metacall_destroy(); + return 1; + } + + // Print memory usage + metacall_value_destroy(metacall("mem_check")); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + ::benchmark::RunSpecifiedBenchmarks(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + // Print memory usage + metacall_value_destroy(metacall("mem_check")); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + + return 0; +} diff --git a/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt b/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt index 6880e4f15a..08605fb08d 100644 --- a/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_py_c_api_bench/CMakeLists.txt @@ -1,11 +1,15 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # External dependencies # -find_package(PythonLibs 3 REQUIRED) +find_package(Python3 COMPONENTS Development) -if(NOT PYTHONLIBS_FOUND) - message(STATUS "Python libraries not found") +if(NOT Python3_Development_FOUND) return() endif() @@ -79,7 +83,7 @@ target_include_directories(${target} ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include - ${PYTHON_INCLUDE_DIRS} # Python includes + ${Python3_INCLUDE_DIRS} # Python includes ) # @@ -92,7 +96,7 @@ target_link_libraries(${target} GBench - ${PYTHON_LIBRARIES} # Python libraries + ${Python3_LIBRARIES} # Python libraries ) # @@ -117,7 +121,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -126,8 +130,27 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # ERROR: LeakSanitizer: detected memory leaks + # + # Direct leak of 449717 byte(s) in 224 object(s) allocated from: + # #0 0x7fd0798519cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fd078cf29c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # Indirect leak of 108494 byte(s) in 110 object(s) allocated from: + # #0 0x7fd0798519cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fd078cf29c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # SUMMARY: AddressSanitizer: 558211 byte(s) leaked in 334 allocation(s). + # + # For solving this, we should enable Python support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json ) # diff --git a/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp b/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp index 2ed434f174..e1db6bb14b 100644 --- a/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp +++ b/source/benchmarks/metacall_py_c_api_bench/source/metacall_py_c_api_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,26 +25,26 @@ class metacall_py_c_api_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State & state) + void SetUp(benchmark::State &state) { static const char buffer[] = - "#!/usr/bin/python3.5\n" + "#!/usr/bin/env python3\n" "def int_mem_type(left: int, right: int) -> int:\n" "\treturn 0;"; - - static const char name[] = "int_mem_type"; - PyObject * dict; + static const char name[] = "int_mem_type"; if (Py_IsInitialized() == 0) { Py_InitializeEx(0); } +#if !(PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9) if (PyEval_ThreadsInitialized() == 0) { PyEval_InitThreads(); } +#endif compiled = Py_CompileString(buffer, name, Py_file_input); @@ -52,21 +52,24 @@ class metacall_py_c_api_bench : public benchmark::Fixture dict = PyModule_GetDict(instance); + Py_INCREF(dict); + func = PyDict_GetItemString(dict, "int_mem_type"); + Py_INCREF(func); + if (!PyCallable_Check(func)) { state.SkipWithError("An error ocurred during script loading"); } - - Py_DECREF(dict); } - void TearDown(benchmark::State & state) + void TearDown(benchmark::State &state) { - Py_DECREF(compiled); - Py_DECREF(instance); Py_DECREF(func); + Py_DECREF(dict); + Py_DECREF(instance); + Py_DECREF(compiled); if (Py_IsInitialized() != 0) { @@ -80,26 +83,25 @@ class metacall_py_c_api_bench : public benchmark::Fixture } protected: - PyObject * compiled; - PyObject * instance; - PyObject * func; + PyObject *compiled; + PyObject *instance; + PyObject *dict; + PyObject *func; }; -BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(long) * 3; // (long, long) -> long for (auto _ : state) { - PyObject * ret; - state.PauseTiming(); - PyObject * tuple_args = PyTuple_New(2); + PyObject *tuple_args = PyTuple_New(2); - PyObject * args[2] = - { + PyObject *args[2] = { PyLong_FromLong(0L), PyLong_FromLong(0L) }; @@ -111,7 +113,7 @@ BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & stat for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = PyObject_CallObject(func, tuple_args)); + PyObject *ret = PyObject_CallObject(func, tuple_args); state.PauseTiming(); @@ -132,8 +134,6 @@ BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & stat state.PauseTiming(); - Py_DECREF(args[0]); - Py_DECREF(args[1]); Py_DECREF(tuple_args); state.ResumeTiming(); @@ -145,9 +145,8 @@ BENCHMARK_DEFINE_F(metacall_py_c_api_bench, call_object)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_py_c_api_bench, call_object) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_MAIN(); \ No newline at end of file +BENCHMARK_MAIN(); diff --git a/source/benchmarks/metacall_py_call_bench/CMakeLists.txt b/source/benchmarks/metacall_py_call_bench/CMakeLists.txt index ec1e7f1a7a..9ed6744229 100644 --- a/source/benchmarks/metacall_py_call_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_py_call_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # Executable name and options # @@ -79,19 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,9 +107,9 @@ target_compile_options(${target} # # Linker options -# +# -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -128,6 +120,15 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader ) # diff --git a/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp b/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp index 7b8eb01f39..68b0bf5890 100644 --- a/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp +++ b/source/benchmarks/metacall_py_call_bench/source/metacall_py_call_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,57 +26,22 @@ class metacall_py_call_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State & state) - { - metacall_print_info(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - { - static const char tag[] = "py"; - - static const char int_mem_type[] = - "#!/usr/bin/python3.5\n" - "def int_mem_type(left: int, right: int) -> int:\n" - "\treturn 0;"; - - if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) - { - state.SkipWithError("Error loading int_mem_type function"); - } - } - #endif /* OPTION_BUILD_LOADERS_PY */ - } - - void TearDown(benchmark::State & state) - { - if (metacall_destroy() != 0) - { - state.SkipWithError("Error destroying MetaCall"); - } - } }; -BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(long) * 3; // (long, long) -> long for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - void * ret; - for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacall("int_mem_type", 0L, 0L)); + void *ret = metacall("int_mem_type", 0L, 0L); state.PauseTiming(); @@ -95,7 +60,7 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args)(benchmark::State & stat state.ResumeTiming(); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Call Benchmark - Variadic Argument Call"); @@ -104,27 +69,24 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_va_args)(benchmark::State & stat } BENCHMARK_REGISTER_F(metacall_py_call_bench, call_va_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args) +(benchmark::State &state) { const int64_t call_count = 1000000; const int64_t call_size = sizeof(long) * 3; // (long, long) -> long for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - void * ret; - state.PauseTiming(); - void * args[2] = - { + void *args[2] = { metacall_value_create_long(0L), metacall_value_create_long(0L) }; @@ -133,7 +95,7 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & s for (int64_t it = 0; it < call_count; ++it) { - benchmark::DoNotOptimize(ret = metacallv("int_mem_type", args)); + void *ret = metacallv("int_mem_type", args); state.PauseTiming(); @@ -161,7 +123,7 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & s state.ResumeTiming(); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Call Benchmark - Array Argument Call"); @@ -170,9 +132,53 @@ BENCHMARK_DEFINE_F(metacall_py_call_bench, call_array_args)(benchmark::State & s } BENCHMARK_REGISTER_F(metacall_py_call_bench, call_array_args) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(5); -BENCHMARK_MAIN(); +/* Use main for initializing MetaCall once. There's a bug in Python async which prevents reinitialization */ +/* https://github.com/python/cpython/issues/89425 */ +/* https://bugs.python.org/issue45262 */ +/* metacall-py-call-benchd: ./Modules/_asynciomodule.c:261: get_running_loop: Assertion `Py_IS_TYPE(rl, &PyRunningLoopHolder_Type)' failed. */ +int main(int argc, char *argv[]) +{ + metacall_print_info(); + + metacall_log_null(); + + if (metacall_initialize() != 0) + { + return 1; + } + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char tag[] = "py"; + + static const char int_mem_type[] = + "#!/usr/bin/env python3\n" + "def int_mem_type(left: int, right: int) -> int:\n" + "\treturn 0;"; + + if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) + { + return 2; + } + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + ::benchmark::Initialize(&argc, argv); + + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) + { + return 3; + } + + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + + metacall_destroy(); + + return 0; +} diff --git a/source/benchmarks/metacall_py_init_bench/CMakeLists.txt b/source/benchmarks/metacall_py_init_bench/CMakeLists.txt index e7963792a6..eb9ecb2c79 100644 --- a/source/benchmarks/metacall_py_init_bench/CMakeLists.txt +++ b/source/benchmarks/metacall_py_init_bench/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # Executable name and options # @@ -79,19 +84,6 @@ target_link_libraries(${target} GBench - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -117,7 +109,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -128,6 +120,15 @@ target_link_libraries(${target} add_test(NAME ${target} COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader ) # diff --git a/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp b/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp index 4413eadbb0..9a1ef4a886 100644 --- a/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp +++ b/source/benchmarks/metacall_py_init_bench/source/metacall_py_init_bench.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,131 +26,121 @@ class metacall_py_init_bench : public benchmark::Fixture { public: - void SetUp(benchmark::State &) - { - metacall_print_info(); - } - - void TearDown(benchmark::State & state) - { - if (metacall_destroy() != 0) - { - state.SkipWithError("Error destroying MetaCall"); - } - } }; -BENCHMARK_DEFINE_F(metacall_py_init_bench, init)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_init_bench, init) +(benchmark::State &state) { for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { + state.PauseTiming(); + + metacall_print_info(); + + metacall_log_null(); + + state.ResumeTiming(); + if (metacall_initialize() != 0) { state.SkipWithError("Error initializing MetaCall"); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Init Benchmark - Init"); } BENCHMARK_REGISTER_F(metacall_py_init_bench, init) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(1); -BENCHMARK_DEFINE_F(metacall_py_init_bench, load)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_init_bench, load) +(benchmark::State &state) { for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char tag[] = "py"; static const char int_mem_type[] = - "#!/usr/bin/python3.5\n" + "#!/usr/bin/env python3\n" "def int_mem_type(left: int, right: int) -> int:\n" "\treturn 0;"; - state.PauseTiming(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } - - state.ResumeTiming(); - if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) { state.SkipWithError("Error loading int_mem_type function"); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Init Benchmark - Load Runtime"); } BENCHMARK_REGISTER_F(metacall_py_init_bench, load) - ->Threads(1) ->Unit(benchmark::kMillisecond) ->Iterations(1) ->Repetitions(1); -BENCHMARK_DEFINE_F(metacall_py_init_bench, load_warm)(benchmark::State & state) +BENCHMARK_DEFINE_F(metacall_py_init_bench, load_warm) +(benchmark::State &state) { for (auto _ : state) { - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char tag[] = "py"; static const char int_a_type[] = - "#!/usr/bin/python3.5\n" + "#!/usr/bin/env python3\n" "def int_a_type(left: int, right: int) -> int:\n" "\treturn 0;"; - static const char int_b_type[] = - "#!/usr/bin/python3.5\n" - "def int_b_type(left: int, right: int) -> int:\n" - "\treturn 0;"; - - state.PauseTiming(); - - if (metacall_initialize() != 0) - { - state.SkipWithError("Error initializing MetaCall"); - } if (metacall_load_from_memory(tag, int_a_type, sizeof(int_a_type), NULL) != 0) { state.SkipWithError("Error loading int_a_type function"); } - - state.ResumeTiming(); - - if (metacall_load_from_memory(tag, int_b_type, sizeof(int_b_type), NULL) != 0) - { - state.SkipWithError("Error loading int_b_type function"); - } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ } state.SetLabel("MetaCall Python Init Benchmark - Load Warm"); } BENCHMARK_REGISTER_F(metacall_py_init_bench, load_warm) - ->Threads(1) ->Unit(benchmark::kMicrosecond) ->Iterations(1) ->Repetitions(1); +BENCHMARK_DEFINE_F(metacall_py_init_bench, destroy) +(benchmark::State &state) +{ + for (auto _ : state) + { +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + metacall_destroy(); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + } + + state.SetLabel("MetaCall Python Init Benchmark - Destroy"); +} + +BENCHMARK_REGISTER_F(metacall_py_init_bench, destroy) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(1); + BENCHMARK_MAIN(); diff --git a/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt b/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt new file mode 100644 index 0000000000..4833bad0de --- /dev/null +++ b/source/benchmarks/metacall_rb_call_bench/CMakeLists.txt @@ -0,0 +1,173 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rb-call-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/metacall_rb_call_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with sanitizers: + # + # Address Sanitizer: + # ERROR: AddressSanitizer: heap-use-after-free on address 0x629000003a50 at pc 0x7fb5059be061 bp 0x6290000032d0 sp 0x629000002a80 + # WRITE of size 22 at 0x629000003a50 thread T0 + # + # Thread Sanitizer: + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14308) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #4 loader_impl_load_from_memory /usr/local/metacall/source/loader/source/loader_impl.c:945 (libmetacalld.so+0x30df1) + # #5 loader_load_from_memory /usr/local/metacall/source/loader/source/loader.c:327 (libmetacalld.so+0x2e1d1) + # #6 metacall_load_from_memory /usr/local/metacall/source/metacall/source/metacall.c:357 (libmetacalld.so+0x32c00) + # #7 metacall_rb_call_bench::SetUp(benchmark::State&) /usr/local/metacall/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp:51 (metacall-rb-call-benchd+0x1a420) + # #8 benchmark::internal::BenchmarkInstance::Run(unsigned long, int, benchmark::internal::ThreadTimer*, benchmark::internal::ThreadManager*, benchmark::internal::PerfCountersMeasurement*) const (metacall-rb-call-benchd+0x5f41f) + # #9 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable Ruby support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp b/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp new file mode 100644 index 0000000000..4dc75e9424 --- /dev/null +++ b/source/benchmarks/metacall_rb_call_bench/source/metacall_rb_call_bench.cpp @@ -0,0 +1,204 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_rb_call_bench : public benchmark::Fixture +{ +public: +}; + +BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_va_args) +(benchmark::State &state) +{ + const int64_t call_count = 1000000; + const int64_t call_size = sizeof(int) * 3; // (int, int) -> int + + for (auto _ : state) + { +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacall("int_mem_type", 0, 0); + + state.PauseTiming(); + + if (ret == NULL) + { + state.SkipWithError("Null return value from int_mem_type"); + } + + if (metacall_value_to_int(ret) != 0) + { + state.SkipWithError("Invalid return value from int_mem_type"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + } +#endif /* OPTION_BUILD_LOADERS_RB */ + } + + state.SetLabel("MetaCall Ruby Call Benchmark - Variadic Argument Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_rb_call_bench, call_va_args) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(5); + +BENCHMARK_DEFINE_F(metacall_rb_call_bench, call_array_args) +(benchmark::State &state) +{ + const int64_t call_count = 1000000; + const int64_t call_size = sizeof(int) * 3; // (int, int) -> int + + for (auto _ : state) + { +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + state.PauseTiming(); + + void *args[2] = { + metacall_value_create_int(0), + metacall_value_create_int(0) + }; + + state.ResumeTiming(); + + for (int64_t it = 0; it < call_count; ++it) + { + void *ret = metacallv("int_mem_type", args); + + state.PauseTiming(); + + if (ret == NULL) + { + state.SkipWithError("Null return value from int_mem_type"); + } + + if (metacall_value_to_int(ret) != 0) + { + state.SkipWithError("Invalid return value from int_mem_type"); + } + + metacall_value_destroy(ret); + + state.ResumeTiming(); + } + + state.PauseTiming(); + + for (auto arg : args) + { + metacall_value_destroy(arg); + } + + state.ResumeTiming(); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + } + + state.SetLabel("MetaCall Ruby Call Benchmark - Array Argument Call"); + state.SetBytesProcessed(call_size * call_count); + state.SetItemsProcessed(call_count); +} + +BENCHMARK_REGISTER_F(metacall_rb_call_bench, call_array_args) + ->Unit(benchmark::kMillisecond) + ->Iterations(1) + ->Repetitions(5); + +/* Use main for initializing MetaCall once. There's a bug in Ruby 3.2 on MacOS which prevents reinitialization */ +/* + Stack trace (most recent call last): + #18 Object "metacall-rb-call-bench", at 0x100363520, in main + 48 + #17 Object "metacall-rb-call-bench", at 0x1003647f8, in benchmark::RunSpecifiedBenchmarks() + 40 + #16 Object "metacall-rb-call-bench", at 0x10036561f, in benchmark::RunSpecifiedBenchmarks(benchmark::BenchmarkReporter*, benchmark::BenchmarkReporter*, std::__1::basic_string, std::__1::allocator >) + 3567 + #15 Object "metacall-rb-call-bench", at 0x10037e408, in benchmark::internal::BenchmarkRunner::DoOneRepetition() + 136 + #14 Object "metacall-rb-call-bench", at 0x10037d97a, in benchmark::internal::BenchmarkRunner::DoNIterations() + 890 + #13 Object "metacall-rb-call-bench", at 0x10037dea7, in benchmark::internal::(anonymous namespace)::RunInThread(benchmark::internal::BenchmarkInstance const*, unsigned long long, int, benchmark::internal::ThreadManager*, benchmark::internal::PerfCountersMeasurement*) + 87 + #12 Object "metacall-rb-call-bench", at 0x10036af5f, in benchmark::internal::BenchmarkInstance::Run(unsigned long long, int, benchmark::internal::ThreadTimer*, benchmark::internal::ThreadManager*, benchmark::internal::PerfCountersMeasurement*) const + 79 + #11 Object "metacall-rb-call-bench", at 0x100363573, in benchmark::Fixture::Run(benchmark::State&) + 19 + #10 Object "metacall-rb-call-bench", at 0x100363605, in metacall_rb_call_bench::SetUp(benchmark::State&) + 69 + #9 Object "libmetacall.dylib", at 0x10050a077, in loader_impl_load_from_memory + 135 + #8 Object "libmetacall.dylib", at 0x10050955f, in loader_impl_initialize + 287 + #7 Object "librb_loader.so", at 0x10058b978, in rb_loader_impl_initialize + 56 + #6 Object "libruby.3.2.dylib", at 0x100c683d7, in ruby_init + 13 + #5 Object "libruby.3.2.dylib", at 0x100c6837b, in ruby_setup + 304 + #4 Object "libruby.3.2.dylib", at 0x100c90ff1, in rb_call_inits + 19 + #3 Object "libruby.3.2.dylib", at 0x100da121e, in Init_TransientHeap + 79 + #2 Object "libsystem_platform.dylib", at 0x7ff8088fbdfc, in _sigtramp + 28 + #1 Object "libbacktrace_plugin.so", at 0x100571b5d, in backward::SignalHandling::sig_handler(int, __siginfo*, void*) + 13 + #0 Object "libbacktrace_plugin.so", at 0x100571bc6, in backward::SignalHandling::handleSignal(int, __siginfo*, void*) + 70 +*/ +int main(int argc, char *argv[]) +{ + metacall_print_info(); + + metacall_log_null(); + + if (metacall_initialize() != 0) + { + return 1; + } + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + static const char tag[] = "rb"; + + static const char int_mem_type[] = + "#!/usr/bin/env ruby\n" + "def int_mem_type(left: Fixnum, right: Fixnum)\n" + "\treturn 0\n" + "end\n"; + + if (metacall_load_from_memory(tag, int_mem_type, sizeof(int_mem_type), NULL) != 0) + { + return 2; + } + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + ::benchmark::Initialize(&argc, argv); + + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) + { + return 3; + } + + ::benchmark::RunSpecifiedBenchmarks(); + ::benchmark::Shutdown(); + + metacall_destroy(); + + return 0; +} diff --git a/source/benchmarks/set_bench/CMakeLists.txt b/source/benchmarks/set_bench/CMakeLists.txt new file mode 100644 index 0000000000..69630fada5 --- /dev/null +++ b/source/benchmarks/set_bench/CMakeLists.txt @@ -0,0 +1,147 @@ +# +# Executable name and options +# + +# Target name +set(target set-bench) +message(STATUS "Benchmark ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/set_bench.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GBench + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::adt +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + --benchmark_out=${CMAKE_BINARY_DIR}/benchmarks/${target}.json +) + +# +# Define dependencies +# + +add_dependencies(${target} + adt +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/benchmarks/set_bench/source/set_bench.cpp b/source/benchmarks/set_bench/source/set_bench.cpp new file mode 100644 index 0000000000..2129c98b54 --- /dev/null +++ b/source/benchmarks/set_bench/source/set_bench.cpp @@ -0,0 +1,147 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#define SET_SIZE 1000 +#define ITERATIONS 1000 + +class set_bench : public benchmark::Fixture +{ +public: + void SetUp(benchmark::State &) + { + s = set_create(&hash_callback_ptr, &comparable_callback_ptr); + + keys.reserve(SET_SIZE); + values.reserve(SET_SIZE); + + for (int i = 0; i < SET_SIZE; ++i) + { + keys.push_back(std::to_string(i)); + values.push_back(i); + set_insert(s, (set_key)keys[i].c_str(), &values[i]); + } + } + + void TearDown(benchmark::State &) + { + set_destroy(s); + } + + set s; + std::vector keys; + std::vector values; +}; + +int set_cb_iterate_sum(set s, set_key key, set_value value, set_cb_iterate_args args) +{ + int *i = (int *)value; + uint64_t *sum = (uint64_t *)args; + + (void)s; + (void)key; + + *sum = ((*sum) + (uint64_t)(*i)); + + return 0; +} + +BENCHMARK_DEFINE_F(set_bench, set_iterate) +(benchmark::State &state) +{ + uint64_t sum = 0; + + for (auto _ : state) + { + set_iterate(s, &set_cb_iterate_sum, &sum); + } + + state.SetLabel("Set Benchmark - Iterate Callback"); + state.SetItemsProcessed(SET_SIZE); +} + +BENCHMARK_REGISTER_F(set_bench, set_iterate) + ->Unit(benchmark::kMillisecond) + ->Iterations(ITERATIONS) + ->Repetitions(3); + +/* +BENCHMARK_DEFINE_F(set_bench, set_iterators) +(benchmark::State &state) +{ + uint64_t sum = 0; + + for (auto _ : state) + { + for (set_iterator it = set_iterator_begin(s); set_iterator_end(&it) > 0; set_iterator_next(it)) + { + int *i = (int *)set_iterator_value(it); + + sum += ((uint64_t)(*i)); + } + } + + (void)sum; + + state.SetLabel("Set Benchmark - Iterators"); + state.SetItemsProcessed(SET_SIZE); +} + +BENCHMARK_REGISTER_F(set_bench, set_iterators) + ->Unit(benchmark::kMillisecond) + ->Iterations(ITERATIONS) + ->Repetitions(3); +*/ + +BENCHMARK_DEFINE_F(set_bench, set_iterators_2) +(benchmark::State &state) +{ + uint64_t sum = 0; + + for (auto _ : state) + { + set_iterator_type it; + + for (set_iterator_begin(&it, s); set_iterator_end(&it) > 0; set_iterator_next(&it)) + { + int *i = (int *)set_iterator_value(&it); + + sum += ((uint64_t)(*i)); + } + } + + (void)sum; + + state.SetLabel("Set Benchmark - Iterators 2"); + state.SetItemsProcessed(SET_SIZE); +} + +BENCHMARK_REGISTER_F(set_bench, set_iterators_2) + ->Unit(benchmark::kMillisecond) + ->Iterations(ITERATIONS) + ->Repetitions(3); + +BENCHMARK_MAIN(); diff --git a/source/cli/CMakeLists.txt b/source/cli/CMakeLists.txt new file mode 100644 index 0000000000..d6b10bf0dc --- /dev/null +++ b/source/cli/CMakeLists.txt @@ -0,0 +1,15 @@ + +# Check if CLIs are enabled +if(NOT OPTION_BUILD_CLI) + return() +endif() + +# Check if the dependency loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE) + message(WARNING "The Extension and NodeJS Loaders are a dependency of the CLI, in order to compile the CLI, enable them with -DOPTION_BUILD_LOADERS_EXT=ON -DOPTION_BUILD_LOADERS_NODE=ON") + return() +endif() + +# CLI applications +add_subdirectory(metacallcli) +add_subdirectory(plugins) diff --git a/source/cli/metacallcli/CMakeLists.txt b/source/cli/metacallcli/CMakeLists.txt new file mode 100644 index 0000000000..8067b8e620 --- /dev/null +++ b/source/cli/metacallcli/CMakeLists.txt @@ -0,0 +1,531 @@ +# +# Executable name and options +# + +# Target name +set(target metacallcli) + +# Exit here if required dependencies are not met +message(STATUS "CLI ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/application.hpp +) + +set(sources + ${source_path}/application.cpp + ${source_path}/main.cpp +) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Target for scripts (testing) +# + +set(PROJECT_METACALL_PORTS_DIRECTORY "${CMAKE_SOURCE_DIR}/source/ports/py_port") + +add_custom_target(${target}-scripts-tests ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${LOADER_SCRIPT_PATH} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/test/ ${LOADER_SCRIPT_PATH}/ + COMMAND ${CMAKE_COMMAND} -DPROJECT_METACALL_PORTS_DIRECTORY=${PROJECT_METACALL_PORTS_DIRECTORY} -DLOADER_SCRIPT_PATH=${LOADER_SCRIPT_PATH} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/configure_tests.cmake +) + +set_target_properties(${target}-scripts-tests + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +add_dependencies(${target} ${target}-scripts-tests) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + ${DEFAULT_INCLUDE_DIRECTORIES} + + PUBLIC + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 # Required for filesystem +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_loader_dependencies(${target} + c_loader + cob_loader + cs_loader + ext_loader + file_loader + java_loader + mock_loader + py_loader + rb_loader + rs_loader + rpc_loader + ts_loader + wasm_loader +) + +add_dependencies(${target} + node_loader + cli_repl_plugin + cli_core_plugin +) + +if(TARGET cli_cmd_plugin) + add_dependencies(${target} + cli_cmd_plugin + ) +endif() + +# +# Deployment +# + +# Executable +install(TARGETS ${target} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT cli + BUNDLE DESTINATION ${INSTALL_BIN} COMPONENT cli +) + +# +# Define test +# + +# Check if tests are enabled +if(NOT OPTION_BUILD_TESTS) + return() +endif() + +# Define variables for testing the CLI interactively +set(TEST_COMMAND_INPUT "${CMAKE_CURRENT_SOURCE_DIR}/test/commands/${target}") +set(TEST_COMMAND_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/test/commands/command_runner.cmake") + +include(TestEnvironmentVariables) + +add_test(NAME ${target} + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +set_tests_properties(${target} PROPERTIES + LABELS ${target} + PASS_REGULAR_EXPRESSION "function three_str\\(a_str, b_str, c_str\\)" +) +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) + +add_test(NAME ${target}-inspect-leak + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-inspect-leak.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +set_property(TEST ${target}-inspect-leak + PROPERTY LABELS ${target}-inspect-leak +) +test_environment_variables(${target}-inspect-leak + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_NODE AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_NODE) + add_test(NAME ${target}-node + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node PROPERTIES + LABELS ${target}-node + PASS_REGULAR_EXPRESSION "4001534" + ) + test_environment_variables(${target}-node + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + if(OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_NODE AND OPTION_BUILD_LOADERS_PY) + add_test(NAME ${target}-node-port-py + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-port-py.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-port-py PROPERTIES + LABELS ${target}-node-port-py + PASS_REGULAR_EXPRESSION "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ) + test_environment_variables(${target}-node-port-py + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + if(OPTION_BUILD_LOADERS_RB) + add_test(NAME ${target}-node-port-py-rb + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-port-py-rb.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_property(TEST ${target}-node-port-py-rb + PROPERTY LABELS ${target}-node-port-py-rb + ) + test_environment_variables(${target}-node-port-py-rb + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() + endif() + + add_test(NAME ${target}-node-null + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-null.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-null PROPERTIES + LABELS ${target}-node-null + PASS_REGULAR_EXPRESSION "Hello 342521512461246!" + ) + test_environment_variables(${target}-node-null + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + add_test(NAME ${target}-node-null-empty + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-null-empty.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-null-empty PROPERTIES + LABELS ${target}-node-null-empty + PASS_REGULAR_EXPRESSION "Hello 342521512461246!" + ) + test_environment_variables(${target}-node-null-empty + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + add_test(NAME ${target}-node-null-undefined + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-node-null-undefined.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-node-null-undefined PROPERTIES + LABELS ${target}-node-null-undefined + PASS_REGULAR_EXPRESSION "(null)" + ) + test_environment_variables(${target}-node-null-undefined + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + if(OPTION_BUILD_LOADERS_PY AND OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_PY) + add_test(NAME ${target}-py-port + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-port.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-port PROPERTIES + LABELS ${target}-py-port + PASS_REGULAR_EXPRESSION "1234" + ) + test_environment_variables(${target}-py-port + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + if(OPTION_BUILD_LOADERS_RB) + add_test(NAME ${target}-py-port-rb + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-port-rb.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-port-rb PROPERTIES + LABELS ${target}-py-port-rb + PASS_REGULAR_EXPRESSION "0123456789ABCDEFasd" + ) + test_environment_variables(${target}-py-port-rb + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() + endif() +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_FILE AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_FILE) + add_test(NAME ${target}-file + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-file.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-file PROPERTIES + LABELS ${target}-file + PASS_REGULAR_EXPRESSION "${LOADER_SCRIPT_PATH}/template.html" + ) + test_environment_variables(${target}-file + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-file-fail + COMMAND $ this-does-not-exist + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-file-fail PROPERTIES + LABELS ${target}-file-fail + PASS_REGULAR_EXPRESSION "Error: Failed to load script 'this-does-not-exist' with loader 'file'" + ) + test_environment_variables(${target}-file-fail + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_PY) + add_test(NAME ${target}-py-naming + COMMAND $ test.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-naming PROPERTIES + LABELS ${target}-py-naming + PASS_REGULAR_EXPRESSION "Test: 66673332" + ) + test_environment_variables(${target}-py-naming + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-py-argv + COMMAND $ cli-test-argv.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-argv PROPERTIES + LABELS ${target}-py-argv + PASS_REGULAR_EXPRESSION "Test: cli-test-argv.py" + ) + test_environment_variables(${target}-py-argv + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-py-main + COMMAND $ cli-test-main.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-main PROPERTIES + LABELS ${target}-py-main + PASS_REGULAR_EXPRESSION "Test: 1234567890abcd" + ) + test_environment_variables(${target}-py-main + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-py-exception # https://github.com/metacall/core/issues/261 + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-exception.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-py-exception PROPERTIES + LABELS ${target}-py-exception + PASS_REGULAR_EXPRESSION "66" + ) + test_environment_variables(${target}-py-exception + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_RB) + add_test(NAME ${target}-rb-simplest + COMMAND $ simplest.rb + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-rb-simplest PROPERTIES + LABELS ${target}-rb-simplest + PASS_REGULAR_EXPRESSION "Hello from Ruby" + ) + test_environment_variables(${target}-rb-simplest + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_TS AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_TS) + add_test(NAME ${target}-ts + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-ts.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc + ) + set_tests_properties(${target}-ts PROPERTIES + LABELS ${target}-ts + PASS_REGULAR_EXPRESSION "51354" + ) + test_environment_variables(${target}-ts + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + add_test(NAME ${target}-tsx-templating + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-tsx-templating.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/templating + ) + set_tests_properties(${target}-tsx-templating PROPERTIES + LABELS ${target}-tsx-templating + PASS_REGULAR_EXPRESSION "Hello metaprogrammer" + ) + test_environment_variables(${target}-tsx-templating + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + + if(NOT (OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS)) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14459) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #9 node_loader_port_load_from_file_export(napi_env__*, napi_callback_info__*) /usr/local/metacall/source/loaders/node_loader/source/node_loader_port.cpp:395 (libnode_loaderd.so+0x113c5) + # #10 (libnode.so.72+0x7b6344) + # #11 node_loader_impl_async_func_call_safe /usr/local/metacall/source/loaders/node_loader/source/node_loader_impl.cpp:2040 (libnode_loaderd.so+0xe2e8) + # #12 (libnode.so.72+0x7b6344) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + add_test(NAME ${target}-tsx-loop-fail + COMMAND $ loopfail.tsx + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/loopfail + ) + set_tests_properties(${target}-tsx-loop-fail PROPERTIES + LABELS ${target}-tsx-loop-fail + PASS_REGULAR_EXPRESSION "Error: Cannot find module 'yeet-oof/whatever'" + ) + test_environment_variables(${target}-tsx-loop-fail + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() + + if(OPTION_BUILD_LOADERS_PY) + add_test(NAME ${target}-py-tsx + COMMAND ${CMAKE_COMMAND} -D "EXECUTABLE=$" -D "INPUT=${TEST_COMMAND_INPUT}-py-tsx.txt" -P ${TEST_COMMAND_RUNNER} + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/templating + ) + set_tests_properties(${target}-py-tsx PROPERTIES + LABELS ${target}-py-tsx + PASS_REGULAR_EXPRESSION "Hello World" + ) + test_environment_variables(${target}-py-tsx + "" + ${TESTS_ENVIRONMENT_VARIABLES} + PROJECT_METACALL_PORTS_DIRECTORY=${CMAKE_SOURCE_DIR}/source/ports/py_port + # Note: Here we have to set up the LOADER_SCRIPT_PATH to the CWD as a difference to ${target}-tsx-templating test + # which does not require this apparently (due to NodeJS resolve system), probably this has to be reviewed + LOADER_SCRIPT_PATH=${LOADER_SCRIPT_PATH}/templating + ) + endif() +endif() + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_EXT AND OPTION_BUILD_EXTENSIONS AND OPTION_BUILD_LOADERS_NODE AND OPTION_BUILD_LOADERS_PY AND OPTION_BUILD_PLUGINS_SANDBOX AND PROJECT_OS_FAMILY STREQUAL unix AND TARGET cli_sandbox_plugin) + if(NOT OPTION_BUILD_THREAD_SANITIZER AND NOT OPTION_BUILD_ADDRESS_SANITIZER) + add_test(NAME ${target}-cmd-sandboxing + COMMAND ${CMAKE_COMMAND} -E env $ --sandboxing --disable_time time.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + set_tests_properties(${target}-cmd-sandboxing PROPERTIES + LABELS ${target}-cmd-sandboxing + WILL_FAIL TRUE + ) + test_environment_variables(${target}-cmd-sandboxing + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ) + endif() +endif() diff --git a/source/cli/metacallcli/README.md b/source/cli/metacallcli/README.md new file mode 100644 index 0000000000..8febe19212 --- /dev/null +++ b/source/cli/metacallcli/README.md @@ -0,0 +1,17 @@ +# MetaCall CLI + +## About + +MetaCall provides a Command Line Interface which supports all MetaCall functionalities such as loading, calling, awaiting scripts. + +Use the `metacall` command in your shell after [installing](https://github.com/metacall/install) MetaCall to launch the CLI. + +## Currently Available Commands + +- `help`: Show help for MetaCall CLI (**Use this command to see the usage of a particular command**) +- `load`: Load a script from file into MetaCall +- `inspect`: Show all runtimes, modules and functions (with their signature) loaded into MetaCall +- `call`: Call a function previously loaded in MetaCall (Note: For `js` and `py` functions that do not return anything, the default return value is a `null`) +- `await`: Await an async function previously loaded in MetaCall +- `clear`: Delete a script previously loaded in MetaCall +- `exit`: Exit from MetaCall CLI diff --git a/source/cli/metacallcli/cmake/configure_tests.cmake b/source/cli/metacallcli/cmake/configure_tests.cmake new file mode 100644 index 0000000000..1898a8275b --- /dev/null +++ b/source/cli/metacallcli/cmake/configure_tests.cmake @@ -0,0 +1,3 @@ +# Configure Python Port path +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/test/cli-test.py.in" "${LOADER_SCRIPT_PATH}/cli-test.py" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/test/cli-test-rb.py.in" "${LOADER_SCRIPT_PATH}/cli-test-rb.py" @ONLY) diff --git a/source/cli/metacallcli/include/metacallcli/application.hpp b/source/cli/metacallcli/include/metacallcli/application.hpp new file mode 100644 index 0000000000..c2e7863a8e --- /dev/null +++ b/source/cli/metacallcli/include/metacallcli/application.hpp @@ -0,0 +1,140 @@ +/* + * MetaCall Command Line Interface by Parra Studios + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * A command line interface example as metacall wrapper. + * + */ + +#ifndef METACALL_CLI_APPLICATION_HPP +#define METACALL_CLI_APPLICATION_HPP 1 + +/* -- Headers -- */ + +#include + +#include +#include +#include +#include +#include + +/* -- Namespace -- */ + +namespace metacallcli +{ +/* -- Class Definition -- */ + +/** +* @brief +* MetaCall command line interface applicaton +*/ +class application +{ +public: + /* -- Public Methods -- */ + + /** + * @brief + * Application class constructor + * + * @param[in] argc + * Number of elements contained in program parameters + * + * @param[in] argv + * Array of strings from program parameters + */ + application(int argc, char *argv[]); + + /** + * @brief + * Application class destructor + */ + ~application(void); + + /** + * @brief + * Application main entry point + */ + void run(void); + +protected: + /* -- Protected Methods -- */ + + /** + * @brief + * Initialize the REPL + */ + void repl(); + + /** + * @brief + * Initialize the CMD + * + * @param[in] arguments + * Vector of strings containing all the arguments from argv + * + * @return + * Return true if the load was successful, false otherwise + */ + bool cmd(std::vector &arguments); + + /** + * @brief + * Fallback argument parser + * + * @param[in] arguments + * Vector of strings containing all the arguments from argv + * + */ + void arguments_parse(std::vector &arguments); + + /** + * @brief + * Load all plugins from a subfolder @path + * + * @param[in] path + * Subpath where the plugins are located + * + * @param[out] handle + * Pointer to the handle containing of the loaded scripts + * + * @return + * Return true if the load was successful, false otherwise + * + */ + bool load_path(const char *path, void **handle); + + /** + * @brief + * Execute a command with string parameters + * + * @param[inout] tokens + * Value of type array containing all the tokens of the input command + * + * @return + * Return result of the command execution + */ + void *execute(void *tokens); + + /** + * @brief + * Check if a value is an exception or throwable, then prints it. + * The method always destroys the value @v + * + * @param[inout] v + * Value to be checked against and destroyed + */ + void check_for_exception(void *v); + +private: + /* -- Private Member Data -- */ + + void *plugin_cli_handle; /**< Handle containing all loaded plugins for CLI */ + void *plugin_repl_handle; /**< Handle containing all loaded plugins for REPL */ + void *plugin_cmd_handle; /**< Handle containing all loaded plugins for CMD */ +}; + +} /* namespace metacallcli */ + +#endif /* METACALL_CLI_APPLICATION_HPP */ diff --git a/source/cli/metacallcli/source/application.cpp b/source/cli/metacallcli/source/application.cpp new file mode 100644 index 0000000000..098f639006 --- /dev/null +++ b/source/cli/metacallcli/source/application.cpp @@ -0,0 +1,573 @@ +/* + * MetaCall Command Line Interface by Parra Studios + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * A command line interface example as metacall wrapper. + * + */ + +/* -- Headers -- */ + +#include + +#if defined __has_include + #if __has_include() + #include +namespace fs = std::filesystem; + #elif __has_include() + #include +namespace fs = std::experimental::filesystem; + #else + #error "Missing the header." + #endif +#else + #error "C++ standard too old for compiling this file." +#endif + +#include +#include +#include + +/* -- Namespace Declarations -- */ + +using namespace metacallcli; + +/* -- Private Data -- */ + +static bool exit_condition = true; + +/* -- Methods -- */ + +void application::repl() +{ + /* Initialize REPL plugins */ + if (!load_path("repl", &plugin_repl_handle)) + { + /* Do not enter into the main loop */ + exit_condition = true; + return; + } + + /* Register exit function */ + auto exit_command = [](size_t argc, void *args[], void *data) -> void * { + (void)args; + (void)data; + + /* Validate function parameters */ + if (argc != 0) + { + std::cout << "Invalid number of arguments passed to exit, expected 0, received: " << argc << std::endl; + } + + std::cout << "Exiting ..." << std::endl; + + /* Exit from main loop */ + exit_condition = true; + + return NULL; + }; + + int result = metacall_register_loaderv(metacall_loader("ext"), plugin_repl_handle, "exit", exit_command, METACALL_INVALID, 0, NULL); + + if (result != 0) + { + std::cout << "Exit function was not registered properly, return code: " << result << std::endl; + exit(1); + } + else + { + /* Start the main loop */ + exit_condition = false; + } + + /* Initialize REPL descriptors */ + std::string plugin_path(metacall_plugin_path()); + + void *args[] = { + metacall_value_create_string(plugin_path.c_str(), plugin_path.length()) + }; + + void *ret = metacallhv_s(plugin_cli_handle, "repl_initialize", args, sizeof(args) / sizeof(args[0])); + + metacall_value_destroy(args[0]); + + check_for_exception(ret); +} + +bool application::cmd(std::vector &arguments) +{ + /* Get the command parsing function */ + void *command_parse_func = metacall_handle_function(plugin_cli_handle, "command_parse"); + + /* By default, when executing the cmd, it will exit of the REPL */ + exit_condition = true; + + if (command_parse_func == NULL) + { + std::cout << "Warning: CLI Arguments Parser was not loaded, " + "using fallback argument parser with positional arguments only. " + << std::endl + << "Any command line option like '--help' will result into error. " + "Only files are allowed: $ metacall a.py b.js c.rb" + << std::endl; + + /* Use fallback parser, it can execute files but does not support command line arguments as options (i.e: -h, --help) */ + /* Parse program arguments if any (e.g metacall (0) a.py (1) b.js (2) c.rb (3)) */ + arguments_parse(arguments); + + return true; + } + + /* Check first if the command function is registered */ + void *command_function_func = metacall_handle_function(plugin_cli_handle, "command_function"); + + if (command_function_func == NULL) + { + return false; + } + + /* Initialize CMD plugins */ + if (!load_path("cmd", &plugin_cmd_handle)) + { + return false; + } + + /* Initialize CMD descriptors */ + std::string plugin_path(metacall_plugin_path()); + + void *command_initialize_args[] = { + metacall_value_create_string(plugin_path.c_str(), plugin_path.length()) + }; + + void *command_initialize_ret = metacallhv_s(plugin_cli_handle, "command_initialize", command_initialize_args, sizeof(command_initialize_args) / sizeof(command_initialize_args[0])); + + check_for_exception(command_initialize_ret); + + for (void *arg : command_initialize_args) + { + metacall_value_destroy(arg); + } + + /* Convert all arguments into metacall value strings */ + std::vector arguments_values; + arguments_values.reserve(arguments.size()); + + for (const std::string &str_argument : arguments) + { + arguments_values.push_back(metacall_value_create_string(str_argument.c_str(), str_argument.length())); + } + + /* Parse the arguments with the CMD plugin command parse function */ + void *ret = metacallfv_s(command_parse_func, arguments_values.data(), arguments_values.size()); + + /* Destroy all the command parse values */ + for (void *value_argument : arguments_values) + { + metacall_value_destroy(value_argument); + } + + /* Check for correct result of command parse */ + if (metacall_value_id(ret) != METACALL_ARRAY) + { + check_for_exception(ret); + return false; + } + + /* Get the argument map and the positional array */ + void **ret_array = metacall_value_to_array(ret); + void **command_map = metacall_value_to_map(ret_array[0]); + size_t command_size = metacall_value_count(ret_array[0]); + void **positional_array = metacall_value_to_array(ret_array[1]); + size_t positional_size = metacall_value_count(ret_array[1]); + + /* Execute arguments */ + for (size_t iterator = 0; iterator < command_size; ++iterator) + { + void **command_pair = metacall_value_to_array(command_map[iterator]); + + void *command_args[] = { + command_pair[0] + }; + + void *command_func = metacallfv_s(command_function_func, command_args, sizeof(command_args) / sizeof(command_args[0])); + + if (metacall_value_id(command_func) == METACALL_FUNCTION) + { + /* Execute the function */ + void *command_ret = metacallfv_s(command_func, &command_pair[1], 1); + metacall_value_destroy(command_func); + check_for_exception(command_ret); + continue; + } + else if (metacall_value_id(command_func) == METACALL_DOUBLE) + { + static const double COMMAND_NOT_REGISTERED = 0.0; + + /* The command is not registered, skip it */ + if (metacall_value_to_double(command_func) == COMMAND_NOT_REGISTERED) + { + metacall_value_destroy(command_func); + continue; + } + + /* If the function is undefined, try to match the command with a function in the handle scope */ + metacall_value_destroy(command_func); + } + else + { + check_for_exception(command_func); + return false; + } + + /* Otherwise use the cmd handle scope for obtaining the function */ + const char *command_str = metacall_value_to_string(command_pair[0]); + command_func = metacall_handle_function(plugin_cmd_handle, command_str); + + if (command_func == NULL) + { + std::cout << "Error: Command line option '" << command_str << "' unrecognized" << std::endl; + return false; + } + + void *command_ret = metacallfv_s(command_func, &command_pair[1], 1); + + check_for_exception(command_ret); + } + + /* Note: If it has zero positional arguments, we should also run the repl, for example: + * $ metacall --some-option --another-option + */ + if (positional_size == 0) + { + /* Initialize the REPL */ + repl(); + exit_condition = false; + } + else + { + /* Execute the positional arguments */ + std::vector positional_arguments; + positional_arguments.reserve(positional_size); + + for (size_t iterator = 0; iterator < positional_size; ++iterator) + { + positional_arguments.push_back(metacall_value_to_string(positional_array[iterator])); + } + + arguments_parse(positional_arguments); + } + + metacall_value_destroy(ret); + + return true; +} + +application::application(int argc, char *argv[]) : + plugin_cli_handle(NULL), plugin_repl_handle(NULL), plugin_cmd_handle(NULL) +{ + /* Initialize MetaCall */ + if (metacall_initialize() != 0) + { + /* Exit from application */ + exit(1); + } + + /* Initialize MetaCall arguments */ + metacall_initialize_args(argc, argv); + + /* Print MetaCall information */ + metacall_print_info(); + + /* Initialize CLI internal plugins */ + if (!load_path("internal", &plugin_cli_handle)) + { + /* Do not enter into the main loop */ + exit_condition = true; + return; + } + + if (argc == 1) + { + /* Launch the REPL */ + repl(); + } + else + { + std::vector arguments(argv + 1, argv + argc); + + /* Launch the CMD (parse arguments) */ + if (!cmd(arguments)) + { + /* TODO: Report something? */ + } + } +} + +application::~application() +{ + metacall_destroy(); +} + +void application::arguments_parse(std::vector &arguments) +{ + /* List of file extensions mapped into loader tags */ + static std::unordered_map extension_to_tag = { + /* Mock Loader */ + { "mock", "mock" }, + /* Python Loader */ + { "py", "py" }, + /* Ruby Loader */ + { "rb", "rb" }, + /* C# Loader */ + { "cs", "cs" }, + { "dll", "cs" }, + { "vb", "cs" }, + /* Cobol Loader */ + { "cob", "cob" }, + { "cbl", "cob" }, + { "cpy", "cob" }, + /* NodeJS Loader */ + { "js", "node" }, + { "node", "node" }, + /* TypeScript Loader */ + { "ts", "ts" }, + { "jsx", "ts" }, + { "tsx", "ts" }, + /* WASM Loader */ + { "wasm", "wasm" }, + { "wat", "wasm" }, + /* Rust Loader */ + { "rs", "rs" }, + /* C Loader */ + { "c", "c" }, + { "h", "c" }, + /* Java Loader */ + { "java", "java" }, + { "jar", "java" }, + /* RPC Loader */ + { "rpc", "rpc" } + + // TODO: Implement handling of duplicated extensions, + // load the file with all loaders of each duplicated extension (trial and error) + + // /* Extension Loader */ + // { "so", "ext" }, + // { "dylib", "ext" }, + // { "dll", "ext" }, + + /* Note: By default js extension uses NodeJS loader instead of JavaScript V8 */ + /* Probably in the future we can differenciate between them, but it is not trivial */ + }; + + for (std::string script : arguments) + { + const std::string tag = extension_to_tag[script.substr(script.find_last_of(".") + 1)]; + const std::string safe_tag = tag != "" ? tag : "file"; /* Use File Loader if the tag is not found */ + + /* Load the script */ + const char *scripts[1] = { + script.c_str() + }; + + if (metacall_load_from_file(safe_tag.c_str(), scripts, 1, NULL) != 0) + { + /* Stop loading more scripts */ + std::cout << "Error: Failed to load script '" << script << "' with loader '" << safe_tag << "'" << std::endl; + exit(1); + } + } +} + +bool application::load_path(const char *path, void **handle) +{ + /* Get core plugin path and handle in order to load cli plugins */ + const char *plugin_path = metacall_plugin_path(); + void *plugin_extension_handle = metacall_plugin_extension(); + + if (plugin_path == NULL || plugin_extension_handle == NULL) + { + return false; + } + + /* Define the cli plugin path as string (core plugin path plus the subpath) */ + fs::path plugin_cli_path(plugin_path); + plugin_cli_path /= "cli"; + plugin_cli_path /= path; + std::string plugin_cli_path_str(plugin_cli_path.string()); + + /* Load cli plugins into plugin cli handle */ + void *args[] = { + metacall_value_create_string(plugin_cli_path_str.c_str(), plugin_cli_path_str.length()), + metacall_value_create_ptr(handle) + }; + + void *ret = metacallhv_s(plugin_extension_handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + + if (ret == NULL) + { + std::cout << "Failed to load CLI plugins from folder: " << plugin_cli_path_str << std::endl; + return false; + } + + if (metacall_value_id(ret) == METACALL_INT && metacall_value_to_int(ret) != 0) + { + std::cout << "Failed to load CLI plugins from folder '" + << plugin_cli_path_str << "' with result: " + << metacall_value_to_int(ret) << std::endl; + + metacall_value_destroy(ret); + return false; + } + + metacall_value_destroy(ret); + + return true; +} + +void application::run() +{ + void *evaluate_func = metacall_handle_function(plugin_cli_handle, "repl_evaluate"); + + while (exit_condition != true) + { + std::mutex await_mutex; + std::condition_variable await_cond; + std::unique_lock lock(await_mutex); + + struct await_data_type + { + void *v; + std::mutex &mutex; + std::condition_variable &cond; + bool exit_condition; + }; + + struct await_data_type await_data = { NULL, await_mutex, await_cond, false }; + + void *future = metacallfv_await_s( + evaluate_func, metacall_null_args, 0, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->exit_condition = true; + await_data->cond.notify_one(); + return NULL; + }, + static_cast(&await_data)); + + await_cond.wait(lock); + + /* Unused */ + metacall_value_destroy(future); + + /* Check if the loop was rejected */ + if (await_data.exit_condition) + { + exit_condition = true; + } + else + { + void **results = metacall_value_to_array(await_data.v); + void *args[2]; + + if (metacall_value_id(results[0]) == METACALL_EXCEPTION || metacall_value_id(results[0]) == METACALL_THROWABLE) + { + args[0] = metacall_value_copy(results[0]); + args[1] = metacall_value_create_null(); + } + else + { + args[0] = metacall_value_create_null(); + + /* Execute command */ + if (metacall_value_id(results[0]) == METACALL_ARRAY) + { + args[1] = execute(results[0]); + } + else + { + args[1] = metacall_value_copy(results[0]); + } + + if (metacall_value_id(args[1]) == METACALL_INVALID) + { + metacall_value_destroy(args[1]); + args[1] = metacall_value_create_null(); + } + } + + /* Invoke next iteration of the REPL */ + void *ret = metacallfv_s(metacall_value_to_function(results[1]), args, 2); + + check_for_exception(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + metacall_value_destroy(await_data.v); + } + + if (plugin_cli_handle != NULL) + { + /* Close REPL */ + void *ret = metacallhv_s(plugin_cli_handle, "repl_close", metacall_null_args, 0); + + check_for_exception(ret); + + /* Get the command destroy function */ + void *command_destroy_func = metacall_handle_function(plugin_cli_handle, "command_destroy"); + + /* Destroy the commands */ + if (command_destroy_func != NULL) + { + /* Parse the arguments with the CMD plugin command parse function */ + ret = metacallfv_s(command_destroy_func, metacall_null_args, 0); + + check_for_exception(ret); + } + } +} + +void *application::execute(void *tokens) +{ + size_t size = metacall_value_count(tokens); + + if (size == 0) + { + return metacall_value_create_null(); + } + else + { + void **tokens_array = metacall_value_to_array(tokens); + void *key = tokens_array[0]; + + return metacallhv_s(plugin_repl_handle, metacall_value_to_string(key), &tokens_array[1], size - 1); + } +} + +void application::check_for_exception(void *v) +{ + struct metacall_exception_type ex; + + if (metacall_error_from_value(v, &ex) == 0) + { + std::cout << "Exception: " << ex.message << std::endl + << ex.stacktrace << std::endl; + } + + metacall_value_destroy(v); +} diff --git a/source/cli/metacallcli/source/main.cpp b/source/cli/metacallcli/source/main.cpp new file mode 100644 index 0000000000..8c41f19157 --- /dev/null +++ b/source/cli/metacallcli/source/main.cpp @@ -0,0 +1,38 @@ +/* + * MetaCall Command Line Interface by Parra Studios + * A command line interface example as metacall wrapper. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +/* -- Namespace Declarations -- */ + +using namespace metacallcli; + +/* -- Methods -- */ + +int main(int argc, char *argv[]) +{ + application app(argc, argv); + + app.run(); + + return 0; +} diff --git a/source/cli/metacallcli/test/.gitignore b/source/cli/metacallcli/test/.gitignore new file mode 100644 index 0000000000..dc919406c8 --- /dev/null +++ b/source/cli/metacallcli/test/.gitignore @@ -0,0 +1,97 @@ +### Django ### +*.log +*.pot +*.pyc +__pycache__/ +local_settings.py +db.sqlite3 +media + +### Python ### +# Byte-compiled / optimized / DLL files +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo + +# Django stuff: + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +.venv/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/source/cli/metacallcli/test/cli-test-argv.py b/source/cli/metacallcli/test/cli-test-argv.py new file mode 100644 index 0000000000..e247d3e6a7 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-argv.py @@ -0,0 +1,4 @@ +# This test verifies that argv works properly in Python Loader +import sys + +print('Test: ' + sys.argv[0]) diff --git a/source/cli/metacallcli/test/cli-test-main.py b/source/cli/metacallcli/test/cli-test-main.py new file mode 100644 index 0000000000..6903efb5c6 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-main.py @@ -0,0 +1,4 @@ +# This test verifies that __name__ == "__main__" works properly in Python Loader + +if __name__ == "__main__": + print('Test: 1234567890abcd') diff --git a/source/cli/metacallcli/test/cli-test-null-undefined.js b/source/cli/metacallcli/test/cli-test-null-undefined.js new file mode 100644 index 0000000000..c452aefb28 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-null-undefined.js @@ -0,0 +1,9 @@ +function test_null_and_undefined(name) { + if (name) { + return `Hello, ${name}`; + } else { + return 'Hello 342521512461246!' + } +} + +module.exports = test_null_and_undefined; diff --git a/source/cli/metacallcli/test/cli-test-onload.js b/source/cli/metacallcli/test/cli-test-onload.js new file mode 100644 index 0000000000..1d15b9c625 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-onload.js @@ -0,0 +1,7 @@ +const { metacall_load_from_memory, metacall } = require('metacall'); + +metacall_load_from_memory('py', 'def sum(a, b):\n\treturn a + b'); +metacall_load_from_memory('rb', 'def mult(a, b)\n\ta * b\nend'); + +metacall('sum', 3, 4) !== 7 && process.exit(1); +metacall('mult', 3, 4) !== 12 && process.exit(1); diff --git a/source/cli/metacallcli/test/cli-test-rb.py.in b/source/cli/metacallcli/test/cli-test-rb.py.in new file mode 100644 index 0000000000..572c11d970 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-rb.py.in @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +def main(): + import os + import sys + import json + + # Insert Python Port folder first in the system path list + sys.path.insert(0, '@PROJECT_METACALL_PORTS_DIRECTORY@') + + from metacall import metacall, metacall_load_from_file, metacall_load_from_memory + + # Load ruby + metacall_load_from_file('rb', ['hello.rb']); + + # Load test from memory + script = ''' +from metacall import metacall +def test(): + return metacall('say_string_without_spaces', 'asd'); +''' + + metacall_load_from_memory('py', script); + +# Run the main +main(); diff --git a/source/cli/metacallcli/test/cli-test-target.py b/source/cli/metacallcli/test/cli-test-target.py new file mode 100644 index 0000000000..e02a2fd45f --- /dev/null +++ b/source/cli/metacallcli/test/cli-test-target.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +# Target function +def a(): + return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' diff --git a/source/cli/metacallcli/test/cli-test.js b/source/cli/metacallcli/test/cli-test.js new file mode 100644 index 0000000000..b7119e20c1 --- /dev/null +++ b/source/cli/metacallcli/test/cli-test.js @@ -0,0 +1,3 @@ +const { a } = require('./cli-test-target.py'); + +console.log(a()); diff --git a/source/cli/metacallcli/test/cli-test.py.in b/source/cli/metacallcli/test/cli-test.py.in new file mode 100644 index 0000000000..c6f01d1e3c --- /dev/null +++ b/source/cli/metacallcli/test/cli-test.py.in @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +def main(): + import os + import sys + import json + + # Insert Python Port folder first in the system path list + sys.path.insert(0, '@PROJECT_METACALL_PORTS_DIRECTORY@') + + from metacall import metacall, metacall_load_from_file, metacall_load_from_memory, metacall_inspect + + # Load mock + metacall_load_from_file('mock', ['test.mock']); + + # Load test from memory + script = ''' +from metacall import metacall +def test(): + # Execute a call to mock previously loaded + return metacall('my_empty_func_int'); +''' + + metacall_load_from_memory('py', script); + + # Execute a call to mock + print(metacall('three_str', 'a', 'b', 'c')); + + # Inspect contents + print(json.dumps(metacall_inspect(), indent = 2)); + +# Run the main +main(); diff --git a/source/cli/metacallcli/test/commands/command_runner.cmake b/source/cli/metacallcli/test/commands/command_runner.cmake new file mode 100644 index 0000000000..297e94ef5a --- /dev/null +++ b/source/cli/metacallcli/test/commands/command_runner.cmake @@ -0,0 +1 @@ +execute_process(COMMAND ${EXECUTABLE} INPUT_FILE ${INPUT}) diff --git a/source/cli/metacallcli/test/commands/metacallcli-file.txt b/source/cli/metacallcli/test/commands/metacallcli-file.txt new file mode 100644 index 0000000000..ea4b5c3e1a --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-file.txt @@ -0,0 +1,4 @@ +load file template.html +inspect +call template.html() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-inspect-leak.txt b/source/cli/metacallcli/test/commands/metacallcli-inspect-leak.txt new file mode 100644 index 0000000000..f7fcfdcc9d --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-inspect-leak.txt @@ -0,0 +1,2 @@ +inspect +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-null-empty.txt b/source/cli/metacallcli/test/commands/metacallcli-node-null-empty.txt new file mode 100644 index 0000000000..37c69a2dd9 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-null-empty.txt @@ -0,0 +1,4 @@ +load node cli-test-null-undefined.js +inspect +call test_null_and_undefined() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-null-undefined.txt b/source/cli/metacallcli/test/commands/metacallcli-node-null-undefined.txt new file mode 100644 index 0000000000..e506b21805 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-null-undefined.txt @@ -0,0 +1,4 @@ +load node cli-test-null-undefined.js +inspect +call test_null_and_undefined(undefined) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-null.txt b/source/cli/metacallcli/test/commands/metacallcli-node-null.txt new file mode 100644 index 0000000000..7a1b7616de --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-null.txt @@ -0,0 +1,4 @@ +load node cli-test-null-undefined.js +inspect +call test_null_and_undefined(null) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-port-py-rb.txt b/source/cli/metacallcli/test/commands/metacallcli-node-port-py-rb.txt new file mode 100644 index 0000000000..d522558a6b --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-port-py-rb.txt @@ -0,0 +1,3 @@ +load node cli-test-onload.js +inspect +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node-port-py.txt b/source/cli/metacallcli/test/commands/metacallcli-node-port-py.txt new file mode 100644 index 0000000000..e601228e4a --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node-port-py.txt @@ -0,0 +1,3 @@ +load node cli-test.js +inspect +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-node.txt b/source/cli/metacallcli/test/commands/metacallcli-node.txt new file mode 100644 index 0000000000..519b7b6c74 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-node.txt @@ -0,0 +1,4 @@ +load node nod.js +inspect +call hello_boy(300, 4001234) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-exception.txt b/source/cli/metacallcli/test/commands/metacallcli-py-exception.txt new file mode 100644 index 0000000000..c120e3648b --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-exception.txt @@ -0,0 +1,5 @@ +load py ducktype.py +inspect +call sum(1, "a") +call sum(33, 33) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-port-rb.txt b/source/cli/metacallcli/test/commands/metacallcli-py-port-rb.txt new file mode 100644 index 0000000000..d6977a74d4 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-port-rb.txt @@ -0,0 +1,4 @@ +load py cli-test-rb.py +inspect +call test() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-port.txt b/source/cli/metacallcli/test/commands/metacallcli-py-port.txt new file mode 100644 index 0000000000..cf3f9744c8 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-port.txt @@ -0,0 +1,4 @@ +load py cli-test.py +inspect +call test() +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-py-tsx.txt b/source/cli/metacallcli/test/commands/metacallcli-py-tsx.txt new file mode 100644 index 0000000000..5d8f83c660 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-py-tsx.txt @@ -0,0 +1,2 @@ +load py cli-test-tsx.py +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-ts.txt b/source/cli/metacallcli/test/commands/metacallcli-ts.txt new file mode 100644 index 0000000000..81067eee1b --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-ts.txt @@ -0,0 +1,4 @@ +load ts typedfunc.ts +inspect +call typed_sum(4, 51350) +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli-tsx-templating.txt b/source/cli/metacallcli/test/commands/metacallcli-tsx-templating.txt new file mode 100644 index 0000000000..90272f632e --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli-tsx-templating.txt @@ -0,0 +1,4 @@ +load ts templating.tsx +inspect +call hello("metaprogrammer") +exit diff --git a/source/cli/metacallcli/test/commands/metacallcli.txt b/source/cli/metacallcli/test/commands/metacallcli.txt new file mode 100644 index 0000000000..bc1fa23086 --- /dev/null +++ b/source/cli/metacallcli/test/commands/metacallcli.txt @@ -0,0 +1,3 @@ +load mock a.mock +inspect +exit diff --git a/source/cli/metacallcli/test/test.py b/source/cli/metacallcli/test/test.py new file mode 100644 index 0000000000..063b1d7055 --- /dev/null +++ b/source/cli/metacallcli/test/test.py @@ -0,0 +1,9 @@ +# This test verifies that Python Loader loads local scripts before global ones +# test.py has the same file name (without extension) as the test module from Python stdlib +# so basically what happens is that Python is seeing an "import test", and loads first the +# stdlib module instead of the test.py, with the refactor done, the local file should be load first + +def multiply_type(a: int, b: int) -> int: + return a * b + +print('Test: ' + str(multiply_type(33336666, 2))) diff --git a/source/cli/metacallcli/test/time.py b/source/cli/metacallcli/test/time.py new file mode 100644 index 0000000000..d181d115dd --- /dev/null +++ b/source/cli/metacallcli/test/time.py @@ -0,0 +1,3 @@ +import time + +time.sleep(1) diff --git a/source/cli/plugins/CMakeLists.txt b/source/cli/plugins/CMakeLists.txt new file mode 100644 index 0000000000..cf76080cee --- /dev/null +++ b/source/cli/plugins/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# NodeJS Dependency +# + +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(SEND_ERROR "NodeJS libraries not found") + return() +endif() + +# Generate output directories for CLI plugins +execute_process( + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/internal" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl" + COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/cmd" +) + +# +# REPL Plugins +# + +add_subdirectory(cli_repl_plugin) +add_subdirectory(cli_core_plugin) + +# +# CMD Plugins +# + +# NodeJS added util.parseArgs([config]) in versions v18.3.0, v16.17.0 +# Check for compatibility, otherwise use fallback command parser in the CLI +if(NOT (NodeJS_VERSION VERSION_GREATER_EQUAL "18.3.0" OR (NodeJS_VERSION_MAJOR LESS 18 AND NodeJS_VERSION VERSION_GREATER_EQUAL "16.17.0"))) + message(WARNING "NodeJS version ${NodeJS_VERSION} does not support CLI command line plugins, at least v18.3.0 or v16.17.0 are required, using fallback command parser") + return() +endif() + +add_subdirectory(cli_cmd_plugin) +add_subdirectory(cli_sandbox_plugin) diff --git a/source/cli/plugins/cli_cmd_plugin/CMakeLists.txt b/source/cli/plugins/cli_cmd_plugin/CMakeLists.txt new file mode 100644 index 0000000000..5e929d8965 --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/CMakeLists.txt @@ -0,0 +1,68 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target cli_cmd_plugin) + +# Exit here if required dependencies are not met +message(STATUS "Plugin ${target}") + +# +# Source +# + +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +# +# Destination +# + +set(PLUGIN_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/plugins/cli/internal/${target}") + +# +# Project Target +# + +add_custom_target(${target} ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/metacall.json ${PLUGIN_OUTPUT_DIRECTORY}/metacall.json + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/cli_cmd_plugin.js ${PLUGIN_OUTPUT_DIRECTORY}/cli_cmd_plugin.js +) + +# +# Target Properties +# + +set_target_properties(${target} + PROPERTIES + FOLDER "${IDE_FOLDER}" +) + +# +# Dependencies +# + +add_dependencies(${target} + plugin_extension + node_loader +) + +# +# Define test +# + +# Check if tests are enabled +if(NOT OPTION_BUILD_TESTS) + return() +endif() + +add_test(NAME ${target} + COMMAND ${NodeJS_EXECUTABLE} test.js + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source +) diff --git a/source/cli/plugins/cli_cmd_plugin/include/cli_cmd_plugin/cli_cmd_plugin.hpp b/source/cli/plugins/cli_cmd_plugin/include/cli_cmd_plugin/cli_cmd_plugin.hpp new file mode 100644 index 0000000000..3a82b78469 --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/include/cli_cmd_plugin/cli_cmd_plugin.hpp @@ -0,0 +1,67 @@ +/* + * CLI Command Plugin by Parra Studios + * A plugin implementing command line functionality for MetaCall CLI. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef CLI_CMD_PLUGIN_H +#define CLI_CMD_PLUGIN_H 1 + +#include + +#define cli_cmd_plugin() metacall_handle("node", "plugins/cli/internal/cli_cmd_plugin/cli_cmd_plugin.js") + +#define cli_cmd_plugin_register(cli_cmd_handle, name, type, destroy) \ + do \ + { \ + const size_t map_size = destroy == NULL ? 1 : 2; \ +\ + void *args[] = { \ + metacall_value_create_string(name, sizeof(name) - 1), \ + metacall_value_create_map(NULL, map_size) \ + }; \ +\ + void **options = metacall_value_to_map(args[1]); \ + options[0] = metacall_value_create_array(NULL, 2); \ + void **type_tupla = metacall_value_to_array(options[0]); \ +\ + static const char type_key[] = "type"; \ + static const char type_value[] = type; \ +\ + type_tupla[0] = metacall_value_create_string(type_key, sizeof(type_key) - 1); \ + type_tupla[1] = metacall_value_create_string(type_value, sizeof(type_value) - 1); \ +\ + if (destroy != NULL) \ + { \ + options[1] = metacall_value_create_array(NULL, 2); \ + void **destroy_tupla = metacall_value_to_array(options[1]); \ +\ + static const char destroy_key[] = "destroy"; \ +\ + destroy_tupla[0] = metacall_value_create_string(destroy_key, sizeof(destroy_key) - 1); \ + destroy_tupla[1] = metacall_value_create_function(metacall_handle_function(handle, destroy)); \ + } \ +\ + void *ret = metacallhv_s(cli_cmd_handle, "command_register", args, sizeof(args) / sizeof(args[0])); \ +\ + metacall_value_destroy(ret); \ + metacall_value_destroy(args[0]); \ + metacall_value_destroy(args[1]); \ +\ + } while (0) + +#endif /* CLI_CMD_PLUGIN_H */ diff --git a/source/cli/plugins/cli_cmd_plugin/source/cli_cmd_plugin.js b/source/cli/plugins/cli_cmd_plugin/source/cli_cmd_plugin.js new file mode 100644 index 0000000000..2c2045dce0 --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/source/cli_cmd_plugin.js @@ -0,0 +1,123 @@ +const util = require('util'); +const path = require('path'); +const fs = require('fs'); + +const options = {}; + +function command_initialize(plugin_path) { + /* Initialize all CMD descriptors + * This will load all plugin descriptors like: + * plugins/cli/cmd/${plugin_name}/${plugin_name}_cmd.js + * + * The expected format is the following (e.g: cli_help_plugin.js): + * module.exports = { + * 'help': { + * short: 'h', + * type: 'METACALL_BOOL' // This field is required + * fn: () => {} // Function will be executed when the command is called + * destroy: () => {} // Function will be executed at the end of the CLI lifecycle + * } + * }; + */ + const cmd_path = path.join(plugin_path, 'cli', 'cmd'); + const files = (() => { + try { + return fs.readdirSync(cmd_path); + } catch (e) { + /* If the directory does not exist, return no files */ + if (e !== undefined && e.code === 'ENOENT') { + return [] + } + + /* Otherwise, rethrow the exception */ + throw e; + } + })(); + + for (const file of files) { + const file_path = path.join(cmd_path, file); + const file_stat = fs.statSync(file_path); + + if (file_stat.isDirectory()) { + const descriptor_path = path.join(file_path, `${file}_cmd.js`); + try { + const descriptor_stat = fs.statSync(descriptor_path); + + if (descriptor_stat.isFile()) { + const descriptor = require(descriptor_path); + options = { ...options, ...descriptor }; + } + } catch (e) { + /* Skip */ + } + } + } +} + +function command_register(cmd, { short, type, multiple, func, destroy }) { + const type_map = { + METACALL_STRING: 'string', + METACALL_BOOL: 'boolean' + }; + + options[cmd] = { + type: type_map[type || 'METACALL_BOOL'] + }; + + if (short) { + options[cmd]['short'] = short; + } + + if (multiple) { + options[cmd]['multiple'] = multiple; + } + + if (func) { + options[cmd]['func'] = func; + } + + if (destroy) { + options[cmd]['destroy'] = destroy; + } +} + +function command_parse(...args) { + const { values, positionals } = util.parseArgs({ + args, + options, + allowPositionals: true + }); + + return [ { ...values }, positionals ]; +} + +function command_function(cmd) { + const COMMAND_NOT_REGISTERED = 0; + const COMMAND_FUNCTION_UNDEFINED = 1; + + if (options[cmd] === undefined) { + return COMMAND_NOT_REGISTERED; + } + + if (options[cmd].func === undefined) { + return COMMAND_FUNCTION_UNDEFINED; + } + + return options[cmd].func; +} + +function command_destroy() { + for (const { destroy } of Object.values(options)) { + if (destroy) { + destroy(); + } + } +} + +module.exports = { + command_initialize, + command_register, + command_parse, + command_function, + command_destroy +}; diff --git a/source/cli/plugins/cli_cmd_plugin/source/metacall.json b/source/cli/plugins/cli_cmd_plugin/source/metacall.json new file mode 100644 index 0000000000..408e5ab54f --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/source/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "cli_cmd_plugin.js" + ] +} diff --git a/source/cli/plugins/cli_cmd_plugin/source/test.js b/source/cli/plugins/cli_cmd_plugin/source/test.js new file mode 100644 index 0000000000..c3f8903ecd --- /dev/null +++ b/source/cli/plugins/cli_cmd_plugin/source/test.js @@ -0,0 +1,15 @@ +const assert = require('assert').strict; +const { command_register, command_parse } = require('./cli_cmd_plugin') + +command_register('empty', {}); +command_register('help', { short: 'h', type: 'METACALL_BOOL' }); +command_register('option', { short: 'o', type: 'METACALL_STRING' }); +command_register('multiple', { short: 'm', type: 'METACALL_STRING', multiple: true }); + +assert.deepEqual(command_parse(), [{}, []]); +assert.deepEqual(command_parse('--empty'), [{ empty: true }, []]); +assert.deepEqual(command_parse('-h'), [{ help: true }, []]); +assert.deepEqual(command_parse('--help'), [{ help: true }, []]); +assert.deepEqual(command_parse('--help', 'a.js'), [{ help: true }, ['a.js']]); +assert.deepEqual(command_parse('--option=hello', 'a.js'), [{ option: 'hello' }, ['a.js']]); +assert.deepEqual(command_parse('--multiple=a', '--multiple=b', '--multiple=c'), [{ multiple: ['a', 'b', 'c'] }, []]); diff --git a/source/cli/plugins/cli_core_plugin/CMakeLists.txt b/source/cli/plugins/cli_core_plugin/CMakeLists.txt new file mode 100644 index 0000000000..680417f11c --- /dev/null +++ b/source/cli/plugins/cli_core_plugin/CMakeLists.txt @@ -0,0 +1,215 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target cli_core_plugin) + +# Exit here if required dependencies are not met +message(STATUS "Plugin ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/cli_core_plugin.h +) + +set(sources + ${source_path}/cli_core_plugin.cpp +) + +set(scripts + ${source_path}/cli_core_plugin_repl.js +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +set(script_group "Script Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) +source_group_by_path(${source_path} "\\\\.js$" + ${source_group} ${scripts}) + +# +# Create library +# + +# Build library +add_library(${target} MODULE + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set(PLUGIN_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/plugins/cli/repl/${target}") + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + BUNDLE $<$:$<$>> + + # Define custom build output directory + LIBRARY_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PLUGIN_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${PLUGIN_OUTPUT_DIRECTORY}" + + RUNTIME_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PLUGIN_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${PLUGIN_OUTPUT_DIRECTORY}" + + ARCHIVE_OUTPUT_DIRECTORY "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PLUGIN_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${PLUGIN_OUTPUT_DIRECTORY}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + + $ # MetaCall includes + + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${META_PROJECT_NAME}::metacall # MetaCall library + + PUBLIC + ${DEFAULT_LIBRARIES} + + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + + INTERFACE +) + +# +# Dependencies +# + +add_dependencies(${target} + plugin_extension + cli_repl_plugin +) + +# +# Configuration +# + +add_custom_target(${target}_config ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${PLUGIN_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/metacall.json ${PLUGIN_OUTPUT_DIRECTORY}/metacall.json + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/cli_core_plugin_repl.js ${PLUGIN_OUTPUT_DIRECTORY}/cli_core_plugin_repl.js +) + +set_target_properties(${target}_config + PROPERTIES + FOLDER "${IDE_FOLDER}" +) diff --git a/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h b/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h new file mode 100644 index 0000000000..5f43a1358d --- /dev/null +++ b/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h @@ -0,0 +1,36 @@ +/* + * CLI Core Plugin by Parra Studios + * A plugin implementing core functionality for MetaCall CLI. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef CLI_CORE_PLUGIN_H +#define CLI_CORE_PLUGIN_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +CLI_CORE_PLUGIN_API int cli_core_plugin(void *loader, void *handle); + +#ifdef __cplusplus +} +#endif + +#endif /* CLI_CORE_PLUGIN_H */ diff --git a/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp b/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp new file mode 100644 index 0000000000..ea19c681d4 --- /dev/null +++ b/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp @@ -0,0 +1,686 @@ +/* + * CLI Core Plugin by Parra Studios + * A plugin implementing core functionality for MetaCall CLI. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Error messages */ +#define LOAD_ERROR "Failed to load a script" +#define INSPECT_ERROR "Failed to inspect MetaCall context" +#define EVAL_ERROR "Failed to evaluate the expression" +#define CALL_ERROR "Failed to call the function" +#define AWAIT_ERROR "Failed to await the function" +#define CLEAR_ERROR "Failed to clear the handle" +#define COPYRIGHT_ERROR "Failed to show the copyright" +#define HELP_ERROR "Failed to show the help" +#define DEBUG_ERROR "Failed to debug the command" + +#define COPYRIGHT_PRINT() \ + do \ + { \ + std::cout << "MetaCall Command Line Interface by Parra Studios" << std::endl; \ + std::cout << "A command line interface for MetaCall Core" << std::endl; \ + std::cout << std::endl; \ + std::cout << "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia " << std::endl; \ + } while (0) + +template +void *extension_function_check_array(const char *error, void *v, std::vector &array, cast_func_type &cast_func, enum metacall_value_id id) +{ + size_t size = metacall_value_count(v); + void **values = metacall_value_to_array(v); + + array.reserve(size); + + for (size_t i = 0; i < size; ++i) + { + if (metacall_value_id(values[i]) != id) + { + std::stringstream ss; + + ss << error << ", calling with wrong type of argument at argument position #" + << (i + 1) << ", expected " << metacall_value_id_name(id) + << ", got " << metacall_value_type_name(values[i]); + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + array.push_back(cast_func(values[i])); + } + + return NULL; +} + +static const std::vector loader_tags(void) +{ + static const std::vector loaders = { + "mock", "py", "node", "rb", "cs", "cob", + "ts", "js", "file", "wasm", "rs", "c", + "rpc", "ext", "java" + }; + + return loaders; +} + +static bool loader_tag_is_valid(const char *tag) +{ + static const std::vector loaders = loader_tags(); + + /* Check if invalid loader tag */ + return std::find(std::begin(loaders), std::end(loaders), tag) != std::end(loaders); +} + +static void *throw_invalid_tag(const char *error, const char *tag) +{ + static const std::vector loaders = loader_tags(); + auto it = std::begin(loaders); + std::stringstream ss; + ss << error << ", " << tag << " is not a valid tag, use: " << *it; + + do + { + ++it; + ss << ", " << *it; + } while (it != std::end(loaders)); + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); +} + +static void value_array_for_each(void *v, const std::function &lambda) +{ + void **v_array = static_cast(metacall_value_to_array(v)); + size_t count = metacall_value_count(v); + + std::for_each(v_array, v_array + count, lambda); +} + +static void value_map_for_each(void *v, const std::function &lambda) +{ + void **v_map = static_cast(metacall_value_to_map(v)); + size_t count = metacall_value_count(v); + + std::for_each(v_map, v_map + count, [&lambda](void *element) { + void **v_element = metacall_value_to_array(element); + lambda(metacall_value_to_string(v_element[0]), v_element[1]); + }); +} + +static void *parse_function_call(const char *error, void *func_str_value, void **func, std::string &func_args) +{ + std::string func_str = metacall_value_to_string(func_str_value); + + if (func_str.find('(') == std::string::npos || func_str.find(')') == std::string::npos) + { + std::stringstream ss; + + ss << error << ", calling function with malformed function call string: " << func_str; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + std::string::size_type first_parenthesis = func_str.find_first_of('('); + std::string::size_type last_parenthesis = func_str.find_first_of(')'); + std::string func_name = func_str.substr(0, first_parenthesis); + + if (first_parenthesis != func_name.size() || last_parenthesis != (func_str.size() - 1)) + { + std::stringstream ss; + + ss << error << ", calling function with malformed parameters: " << func_str; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + /* Convert arguments into an array */ + func_args = "["; + func_args += func_str.substr(first_parenthesis + 1, func_str.size() - first_parenthesis - 2); + func_args += "]"; + + /* Check if function is loaded */ + const char *func_name_str = const_cast(func_name.c_str()); + *func = metacall_function(func_name_str); + + if (*func == NULL) + { + std::stringstream ss; + + ss << error << ", function '" << func_name << "' does not exist"; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + return NULL; +} + +void *load(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(LOAD_ERROR, METACALL_STRING, METACALL_ARRAY); + + char *tag = metacall_value_to_string(args[0]); + + if (!loader_tag_is_valid(tag)) + { + return throw_invalid_tag(LOAD_ERROR, tag); + } + + std::vector scripts; + void *error = extension_function_check_array(LOAD_ERROR, args[1], scripts, metacall_value_to_string, METACALL_STRING); + + if (error != NULL) + { + return error; + } + + std::vector scripts_cstr; + + scripts_cstr.reserve(scripts.size()); + + for (auto it = std::begin(scripts); it != std::end(scripts); ++it) + { + scripts_cstr.push_back((*it).c_str()); + } + + int ret = metacall_load_from_file(tag, scripts_cstr.data(), scripts_cstr.size(), NULL); + + return metacall_value_create_int(ret); +} + +void *inspect(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(INSPECT_ERROR); + + void *v = metacall_inspect_value(); + + if (v == NULL) + { + EXTENSION_FUNCTION_THROW(INSPECT_ERROR ", inspect returned invalid value"); + } + + /* Print run-times */ + value_map_for_each(v, [](const char *key, void *modules) { + std::cout << "runtime " << key; + + if (metacall_value_count(modules) == 0) + { + std::cout << std::endl; + return; + } + + std::cout << " {" << std::endl; + + /* Print scripts */ + value_array_for_each(modules, [](void *module) { + /* Get module name */ + void **v_module_map = static_cast(metacall_value_to_map(module)); + void **v_module_name_tuple = metacall_value_to_array(v_module_map[0]); + const char *name = metacall_value_to_string(v_module_name_tuple[1]); + + std::cout << "\tmodule " << name << " { " << std::endl; + + /* Get module functions */ + void **v_module_scope_tuple = metacall_value_to_array(v_module_map[1]); + void **v_scope_map = metacall_value_to_map(v_module_scope_tuple[1]); + void **v_scope_funcs_tuple = metacall_value_to_array(v_scope_map[1]); + + if (metacall_value_count(v_scope_funcs_tuple[1]) != 0) + { + value_array_for_each(v_scope_funcs_tuple[1], [](void *func) { + /* Get function name */ + void **v_func_map = static_cast(metacall_value_to_map(func)); + void **v_func_tuple = metacall_value_to_array(v_func_map[0]); + const char *func_name = metacall_value_to_string(v_func_tuple[1]); + + std::cout << "\t\tfunction " << func_name << "("; + + /* Get function signature */ + void **v_signature_tuple = metacall_value_to_array(v_func_map[1]); + void **v_args_map = metacall_value_to_map(v_signature_tuple[1]); + void **v_args_tuple = metacall_value_to_array(v_args_map[1]); + void **v_args_array = metacall_value_to_array(v_args_tuple[1]); + + size_t iterator = 0, count = metacall_value_count(v_args_tuple[1]); + + value_array_for_each(v_args_array, [&iterator, &count](void *arg) { + void **v_arg_map = metacall_value_to_map(arg); + void **v_arg_name_tupla = metacall_value_to_array(v_arg_map[0]); + std::string parameter_name(metacall_value_to_string(v_arg_name_tupla[1])); + + if (parameter_name.empty()) + { + parameter_name += "arg"; + parameter_name += std::to_string(iterator); + } + + std::cout << parameter_name; + + if (iterator + 1 < count) + { + std::cout << ", "; + } + + ++iterator; + }); + + std::cout << ")" << std::endl; + }); + } + + std::cout << "\t}" << std::endl; + }); + + std::cout << "}" << std::endl; + }); + + metacall_value_destroy(v); + + return metacall_value_create_int(0); +} + +void *eval(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(EVAL_ERROR, METACALL_STRING, METACALL_STRING); + + char *tag = metacall_value_to_string(args[0]); + + if (!loader_tag_is_valid(tag)) + { + return throw_invalid_tag(EVAL_ERROR, tag); + } + + char *script = metacall_value_to_string(args[1]); + size_t size = metacall_value_size(args[1]); + + return metacall_value_create_int(metacall_load_from_memory(tag, script, size, NULL)); +} + +void *call(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(CALL_ERROR, METACALL_STRING); + + /* Parse function call */ + void *func = NULL; + std::string func_args; + void *error = parse_function_call(CALL_ERROR, args[0], &func, func_args); + + if (error != NULL) + { + return error; + } + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + void *result = metacallfs(func, func_args.c_str(), func_args.length() + 1, allocator); + + metacall_allocator_destroy(allocator); + + return result; +} + +void *await(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(CALL_ERROR, METACALL_STRING); + + /* Parse function call */ + void *func = NULL; + std::string func_args; + void *error = parse_function_call(AWAIT_ERROR, args[0], &func, func_args); + + if (error != NULL) + { + return error; + } + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + std::mutex await_mutex; /* Mutex for blocking the process until await is resolved */ + std::condition_variable await_cond; /* Condition to be fired once await method is resolved or rejected */ + + std::unique_lock lock(await_mutex); + + struct await_data_type + { + void *v; + std::mutex &mutex; + std::condition_variable &cond; + }; + + struct await_data_type fdata = { NULL, await_mutex, await_cond }; + + void *future = metacallfs_await( + func, func_args.c_str(), func_args.length() + 1, allocator, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + static_cast(&fdata)); + + await_cond.wait(lock); + + /* Unused */ + metacall_value_destroy(future); + + metacall_allocator_destroy(allocator); + + return fdata.v; +} + +void *clear(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(CLEAR_ERROR, METACALL_STRING, METACALL_STRING); + + char *tag = metacall_value_to_string(args[0]); + + if (!loader_tag_is_valid(tag)) + { + return throw_invalid_tag(CLEAR_ERROR, tag); + } + + char *script = metacall_value_to_string(args[1]); + void *handle = metacall_handle(tag, script); + + if (handle == NULL) + { + std::stringstream ss; + + ss << CLEAR_ERROR ", handle '" << script << "' was not found in loader with tag: " << tag; + + EXTENSION_FUNCTION_THROW(ss.str().c_str()); + } + + return metacall_value_create_int(metacall_clear(handle)); +} + +void *copyright(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(COPYRIGHT_ERROR); + + COPYRIGHT_PRINT(); + + return metacall_value_create_int(0); +} + +void *help(size_t argc, void *args[], void *data) +{ + /* Validate function parameters */ + EXTENSION_FUNCTION_CHECK(HELP_ERROR); + + /* Print copyright first */ + COPYRIGHT_PRINT(); + + /* Command list */ + std::cout << std::endl + << "Command list:" << std::endl + << std::endl; + + /* Load command */ + std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "\t│ Load a script from file into MetaCall │" << std::endl; + std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; + std::cout << "\t│ Usage: │" << std::endl; + std::cout << "\t│ load ... │" << std::endl; + std::cout << "\t│ : identifier to the type of script │" << std::endl; + std::cout << "\t│ options : │" << std::endl; + std::cout << "\t│ mock - Mock (for testing purposes) │" << std::endl; + std::cout << "\t│ py - Python │" << std::endl; + std::cout << "\t│ node - NodeJS │" << std::endl; + std::cout << "\t│ rb - Ruby │" << std::endl; + std::cout << "\t│ cs - C# NetCore │" << std::endl; + std::cout << "\t│ cob - Cobol │" << std::endl; + std::cout << "\t│ ts - TypeScript │" << std::endl; + std::cout << "\t│ js - V8 JavaScript Engine │" << std::endl; + std::cout << "\t│ file - Files (for handling file systems) │" << std::endl; + std::cout << "\t│ ... : relative or absolute path to the script(s) │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Example: │" << std::endl; + std::cout << "\t│ load node concat.js │" << std::endl; + std::cout << "\t│ load py example.py │" << std::endl; + std::cout << "\t│ load rb hello.rb │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Result: │" << std::endl; + std::cout << "\t│ Script (concat.js) loaded correctly │" << std::endl; + std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl + << std::endl; + + /* Inspect command */ + std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "\t│ Show all runtimes, modules and functions (with their signature) loaded into MetaCall │" << std::endl; + std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; + std::cout << "\t│ Usage: │" << std::endl; + std::cout << "\t│ inspect │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Example: │" << std::endl; + std::cout << "\t│ inspect │" << std::endl; + std::cout << "\t│ │" << std::endl; + std::cout << "\t│ Result: │" << std::endl; + std::cout << "\t│ runtime node { │" << std::endl; + std::cout << "\t│ module concat { │" << std::endl; + std::cout << "\t│ function concat(left, right) │" << std::endl; + std::cout << "\t│ } │" << std::endl; + std::cout << "\t│ } │" << std::endl; + std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl + << std::endl; + + /* Eval command */ + std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; + std::cout << "\t│ Evaluate a code snippet with the specified runtime tag │" << std::endl; + std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; + std::cout << "\t│ Usage: │" << std::endl; + std::cout << "\t│ eval + + + ); +} + +export default index; diff --git a/source/scripts/typescript/templating/CMakeLists.txt b/source/scripts/typescript/templating/CMakeLists.txt new file mode 100644 index 0000000000..ca2e15fc9e --- /dev/null +++ b/source/scripts/typescript/templating/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# External dependencies +# + +find_package(NPM) + +if(NOT NPM_FOUND) + message(SEND_ERROR "NPM not found") + return() +endif() + +# Dependencies +add_custom_target(typescript-templating-depends + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source/templating + COMMAND ${NPM_EXECUTABLE} install + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/templating/package.json ${CMAKE_CURRENT_SOURCE_DIR}/source/templating/package-lock.json +) + +set_target_properties(typescript-templating-depends + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}/TypeScript" +) + +# +# Configure typescript project +# + +ts_project(templating 0.1.0) + +add_dependencies(typescript-templating typescript-templating-depends) diff --git a/source/scripts/typescript/templating/source/templating/.gitignore b/source/scripts/typescript/templating/source/templating/.gitignore new file mode 100644 index 0000000000..982ce2624d --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/.gitignore @@ -0,0 +1,119 @@ +# Build directory +build + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/source/scripts/typescript/templating/source/templating/cli-test-tsx.py b/source/scripts/typescript/templating/source/templating/cli-test-tsx.py new file mode 100644 index 0000000000..8b82e45207 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/cli-test-tsx.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +# This script is here in order to reproduce the bug of python-tsx-example + +def main(): + import sys + import os + + # Insert Python Port folder first in the system path list + sys.path.insert(0, os.environ['PROJECT_METACALL_PORTS_DIRECTORY']) + + import metacall + from templating.tsx import hello + print(hello('World')) + +# Run the main +main(); diff --git a/source/scripts/typescript/templating/source/templating/package-lock.json b/source/scripts/typescript/templating/source/templating/package-lock.json new file mode 100644 index 0000000000..2f4aa09878 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/package-lock.json @@ -0,0 +1,120 @@ +{ + "name": "templating-tsx", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "templating-tsx", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + } +} diff --git a/source/scripts/typescript/templating/source/templating/package.json b/source/scripts/typescript/templating/source/templating/package.json new file mode 100644 index 0000000000..1a182f9e45 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/package.json @@ -0,0 +1,13 @@ +{ + "name": "templating-tsx", + "version": "1.0.0", + "main": "templating.tsx", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "license": "Apache-2.0", + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } +} diff --git a/source/scripts/typescript/templating/source/templating/templating.tsx b/source/scripts/typescript/templating/source/templating/templating.tsx new file mode 100644 index 0000000000..a2e102d5df --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/templating.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { renderToString } from 'react-dom/server'; + +export function hello(text: string): string { + return renderToString(

{`Hello ${text}`}

); +} diff --git a/source/scripts/typescript/templating/source/templating/tsconfig.json b/source/scripts/typescript/templating/source/templating/tsconfig.json new file mode 100644 index 0000000000..0bc8548d83 --- /dev/null +++ b/source/scripts/typescript/templating/source/templating/tsconfig.json @@ -0,0 +1,12 @@ +{ + "include": [ + "templating.tsx" + ], + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "esModuleInterop": true, + "jsx": "react", + "lib": ["es2016", "dom"] + } +} diff --git a/source/scripts/typescript/typedfunc/CMakeLists.txt b/source/scripts/typescript/typedfunc/CMakeLists.txt new file mode 100644 index 0000000000..131e733d8c --- /dev/null +++ b/source/scripts/typescript/typedfunc/CMakeLists.txt @@ -0,0 +1,5 @@ +# +# Configure typescript project +# + +ts_project(typedfunc 0.1.0) diff --git a/source/scripts/typescript/typedfunc/source/typedfunc/typedfunc.ts b/source/scripts/typescript/typedfunc/source/typedfunc/typedfunc.ts new file mode 100644 index 0000000000..9bad24df2a --- /dev/null +++ b/source/scripts/typescript/typedfunc/source/typedfunc/typedfunc.ts @@ -0,0 +1,25 @@ +'use strict'; + +export function typed_sum(left: number, right: number): number { + return left + right; +} + +export async function typed_sum_async(left: number, right: number): Promise { + return left + right; +} + +export function build_name(first: string, last = 'Smith') { + return `${first} ${last}`; +} + +export function object_pattern_ts({ asd }) { + return asd; +} + +export function typed_array(a: number[]): number { + return a[0] + a[1] + a[2]; +} + +export function object_record(a: Record): number { + return a.element; +} diff --git a/source/scripts/wasm/CMakeLists.txt b/source/scripts/wasm/CMakeLists.txt new file mode 100644 index 0000000000..34bff0bba5 --- /dev/null +++ b/source/scripts/wasm/CMakeLists.txt @@ -0,0 +1,14 @@ +# TODO: Automatically call wat2wasm on scripts when changed + +# Check if this script is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_WASM OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_WASM) + return() +endif() + +# Append cmake path +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# Wasm project utility +include(WasmProject) + +add_subdirectory(tests) diff --git a/source/scripts/wasm/cmake/WasmProject.cmake b/source/scripts/wasm/cmake/WasmProject.cmake new file mode 100644 index 0000000000..aa326a7401 --- /dev/null +++ b/source/scripts/wasm/cmake/WasmProject.cmake @@ -0,0 +1,51 @@ +# +# WebAssembly project generator by Parra Studios +# Generates a WebAssembly project embedded into CMake. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if(WASMPROJECT_FOUND) + return() +endif() + +set(WASMPROJECT_FOUND YES) + +# +# Generic script project generator +# + +include(ScriptProject) + +# Define current Wasm project configuration path +get_filename_component(WASM_PROJECT_CONFIG_PATH ${CMAKE_CURRENT_LIST_FILE} PATH) + +# +# Wasm sub-project util function +# + +function(wasm_project target version) + + # TODO + + # Configuration + set(PACKAGE_NAME ${target}) + set(PACKAGE_VERSION ${version}) + set(PACKAGE_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") + + # Create project file + script_project(${target} wasm ${WASM_PROJECT_CONFIG_PATH}/WasmProject.cmake.in) + +endfunction() diff --git a/source/scripts/wasm/cmake/WasmProject.cmake.in b/source/scripts/wasm/cmake/WasmProject.cmake.in new file mode 100644 index 0000000000..630c875bea --- /dev/null +++ b/source/scripts/wasm/cmake/WasmProject.cmake.in @@ -0,0 +1,35 @@ +# +# WebAssembly project generator by Parra Studios +# Generates a WebAssembly project embedded into CMake. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Check if this loader is enabled +if(NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_WASM) + return() +endif() + +# +# External dependencies +# + +# TODO + +# Target name +set(target @PACKAGE_NAME@) + +# Exit here if required dependencies are not met +message(STATUS "Script ${target}") diff --git a/source/scripts/wasm/tests/CMakeLists.txt b/source/scripts/wasm/tests/CMakeLists.txt new file mode 100644 index 0000000000..f7706402b9 --- /dev/null +++ b/source/scripts/wasm/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +# +# Configure Wasm project +# + +wasm_project(tests 0.1.0) diff --git a/source/scripts/wasm/tests/source/empty_module.wasm b/source/scripts/wasm/tests/source/empty_module.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/source/scripts/wasm/tests/source/empty_module.wasm differ diff --git a/source/scripts/wasm/tests/source/empty_module.wat b/source/scripts/wasm/tests/source/empty_module.wat new file mode 100644 index 0000000000..3af8f25454 --- /dev/null +++ b/source/scripts/wasm/tests/source/empty_module.wat @@ -0,0 +1 @@ +(module) diff --git a/source/scripts/wasm/tests/source/exports1.wat b/source/scripts/wasm/tests/source/exports1.wat new file mode 100644 index 0000000000..6dc3ff52c6 --- /dev/null +++ b/source/scripts/wasm/tests/source/exports1.wat @@ -0,0 +1,9 @@ +(module + (memory (export "memory") 1) + (table (export "table") 1 funcref) + (global (export "immutable_global") i32 (i32.const 1)) + (global (export "mutable_global") (mut i32) (i32.const 1)) + (func (export "duplicate_func") (result i32) + i32.const 1 + ) +) diff --git a/source/scripts/wasm/tests/source/exports2.wat b/source/scripts/wasm/tests/source/exports2.wat new file mode 100644 index 0000000000..92375ce9ec --- /dev/null +++ b/source/scripts/wasm/tests/source/exports2.wat @@ -0,0 +1,5 @@ +(module + (func (export "duplicate_func") (result i64) + i64.const 2 + ) +) diff --git a/source/scripts/wasm/tests/source/functions.wasm b/source/scripts/wasm/tests/source/functions.wasm new file mode 100644 index 0000000000..206383a061 Binary files /dev/null and b/source/scripts/wasm/tests/source/functions.wasm differ diff --git a/source/scripts/wasm/tests/source/functions.wat b/source/scripts/wasm/tests/source/functions.wat new file mode 100644 index 0000000000..655312543d --- /dev/null +++ b/source/scripts/wasm/tests/source/functions.wat @@ -0,0 +1,18 @@ +(module + (func (export "none_ret_none")) + (func (export "i64_ret_none") (param i64)) + (func (export "i32_f32_i64_f64_ret_none") (param i32) (param f32) (param i64) (param f64)) + (func (export "none_ret_i32") (result i32) + i32.const 1) + (func (export "none_ret_i32_f32_i64_f64") (result i32 f32 i64 f64) + i32.const 1 + f32.const 2 + i64.const 3 + f64.const 4) + (func (export "i32_f32_i64_f64_ret_i32_f32_i64_f64") (param i32) (param f32) (param i64) (param f64) (result i32 f32 i64 f64) + i32.const 1 + f32.const 2 + i64.const 3 + f64.const 4) + (func (export "trap") (unreachable)) +) diff --git a/source/scripts/wasm/tests/source/imports.wat b/source/scripts/wasm/tests/source/imports.wat new file mode 100644 index 0000000000..603144454d --- /dev/null +++ b/source/scripts/wasm/tests/source/imports.wat @@ -0,0 +1,12 @@ +(module + (import "exports1" "memory" (memory 1)) + (import "exports1" "table" (table 1 funcref)) + (import "exports1" "immutable_global" (global i32)) + (import "exports1" "mutable_global" (global (mut i32))) + + (import "exports1" "duplicate_func" (func $duplicate_func_i32 (result i32))) + (import "exports2" "duplicate_func" (func $duplicate_func_i64 (result i64))) + + (func (export "duplicate_func_i32") (result i32) (call $duplicate_func_i32)) + (func (export "duplicate_func_i64") (result i64) (call $duplicate_func_i64)) +) diff --git a/source/scripts/wasm/tests/source/invalid_module.wasm b/source/scripts/wasm/tests/source/invalid_module.wasm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source/serial/CMakeLists.txt b/source/serial/CMakeLists.txt index 6595bd2fa5..87de042a14 100644 --- a/source/serial/CMakeLists.txt +++ b/source/serial/CMakeLists.txt @@ -10,7 +10,6 @@ message(STATUS "Lib ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -35,17 +34,12 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(headers ${include_path}/serial.h - ${include_path}/serial_impl.h - ${include_path}/serial_impl_handle.h + ${include_path}/serial_handle.h ${include_path}/serial_interface.h - ${include_path}/serial_singleton.h - ${include_path}/serial_host.h ) set(sources ${source_path}/serial.c - ${source_path}/serial_impl.c - ${source_path}/serial_singleton.c ) # Group source files @@ -70,30 +64,7 @@ add_library(${target} add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects -if(NOT OPTION_BUILD_DIST_LIBS) - export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -endif() - -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) # Create API export header generate_export_header(${target} @@ -140,11 +111,14 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::memory + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect ${META_PROJECT_NAME}::dynlink + ${META_PROJECT_NAME}::plugin + ${META_PROJECT_NAME}::reflect PUBLIC ${DEFAULT_LIBRARIES} @@ -158,6 +132,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE + ${target_upper}_EXPORTS # Export API PUBLIC $<$>:${target_upper}_STATIC_DEFINE> @@ -183,7 +158,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC @@ -191,17 +166,3 @@ target_link_libraries(${target} INTERFACE ) - -# -# Deployment -# - -# Library -if(NOT OPTION_BUILD_DIST_LIBS) - install(TARGETS ${target} - EXPORT "${target}-export" COMPONENT dev - RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime - LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime - ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev - ) -endif() diff --git a/source/serial/include/serial/serial.h b/source/serial/include/serial/serial.h index 9e3ca939dd..f10f9ea761 100644 --- a/source/serial/include/serial/serial.h +++ b/source/serial/include/serial/serial.h @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -15,19 +15,17 @@ #include +#include + #include #ifdef __cplusplus extern "C" { #endif -/* -- Forward Declarations -- */ - -struct serial_type; - /* -- Type Definitions -- */ -typedef struct serial_type * serial; +typedef plugin serial; /* -- Methods -- */ @@ -52,7 +50,7 @@ SERIAL_API int serial_initialize(void); * Pointer to serial on correct initialization, null otherwise * */ -SERIAL_API serial serial_create(const char * name); +SERIAL_API serial serial_create(const char *name); /** * @brief @@ -65,7 +63,7 @@ SERIAL_API serial serial_create(const char * name); * Static const string with serial extension * */ -SERIAL_API const char * serial_extension(serial s); +SERIAL_API const char *serial_extension(serial s); /** * @brief @@ -78,7 +76,7 @@ SERIAL_API const char * serial_extension(serial s); * Static const string with serial name * */ -SERIAL_API const char * serial_name(serial s); +SERIAL_API const char *serial_name(serial s); /** * @brief @@ -103,7 +101,7 @@ SERIAL_API const char * serial_name(serial s); * String with the value serialized on correct serialization, null otherwise * */ -SERIAL_API char * serial_serialize(serial s, value v, size_t * size, memory_allocator allocator); +SERIAL_API char *serial_serialize(serial s, value v, size_t *size, memory_allocator allocator); /** * @brief @@ -125,7 +123,7 @@ SERIAL_API char * serial_serialize(serial s, value v, size_t * size, memory_allo * Pointer to value deserialized on correct serialization, null otherwise * */ -SERIAL_API value serial_deserialize(serial s, const char * buffer, size_t size, memory_allocator allocator); +SERIAL_API value serial_deserialize(serial s, const char *buffer, size_t size, memory_allocator allocator); /** * @brief @@ -149,7 +147,7 @@ SERIAL_API void serial_destroy(void); * Static string containing module information * */ -SERIAL_API const char * serial_print_info(void); +SERIAL_API const char *serial_print_info(void); #ifdef __cplusplus } diff --git a/source/serial/include/serial/serial_handle.h b/source/serial/include/serial/serial_handle.h new file mode 100644 index 0000000000..9c049fcbca --- /dev/null +++ b/source/serial/include/serial/serial_handle.h @@ -0,0 +1,40 @@ +/* + * Serial Library by Parra Studios + * A cross-platform library for managing multiple serialization and deserialization formats. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef SERIAL_HANDLE_H +#define SERIAL_HANDLE_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Type Definitions -- */ + +typedef void *serial_handle; + +#ifdef __cplusplus +} +#endif + +#endif /* SERIAL_HANDLE_H */ diff --git a/source/serial/include/serial/serial_host.h b/source/serial/include/serial/serial_host.h deleted file mode 100644 index e5f966b773..0000000000 --- a/source/serial/include/serial/serial_host.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Serial Library by Parra Studios - * A cross-platform library for managing multiple serialization and deserialization formats. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef SERIAL_HOST_H -#define SERIAL_HOST_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Forward Declarations -- */ - -struct serial_host_type; - -/* -- Type Definitions -- */ - -typedef struct serial_host_type * serial_host; - -/* -- Member Data -- */ - -/** -* @brief -* Structure holding host context from serial -*/ -struct serial_host_type -{ - void * log; -}; - -#ifdef __cplusplus -} -#endif - -#endif /* SERIAL_HOST_H */ diff --git a/source/serial/include/serial/serial_impl.h b/source/serial/include/serial/serial_impl.h deleted file mode 100644 index fafbde58e2..0000000000 --- a/source/serial/include/serial/serial_impl.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -* Serial Library by Parra Studios -* Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -* -* A cross-platform library for managing multiple serialization and deserialization formats. -* -*/ - -#ifndef SERIAL_IMPL_H -#define SERIAL_IMPL_H 1 - -/* -- Headers -- */ - -#include - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Forward Declarations -- */ - -struct serial_impl_type; - -/* -- Type Definitions -- */ - -typedef struct serial_impl_type * serial_impl; - -/* -- Methods -- */ - -/** -* @brief -* Create serial implementation -* -* @return -* Returns pointer to serial implementation correct creation, null otherwise -* -*/ -SERIAL_API serial_impl serial_impl_create(void); - -/** -* @brief -* Retrieve extension supported by the serial implementation -* -* @param[in] impl -* Pointer to serial implementation instance -* -* @return -* Returns constant string representing serial extension -* -*/ -SERIAL_API const char * serial_impl_extension(serial_impl impl); - -/** -* @brief -* Load serial implementation @impl -* -* @param[in] impl -* Pointer to the serial implementation to be loaded -* -* @param[in] path -* Path where dependency is located -* -* @param[in] name -* Dependency name to be injected -* -* @return -* Returns zero on correct loading, distinct from zero otherwise -* -*/ -SERIAL_API int serial_impl_load(serial_impl impl, const char * path, const char * name); - -/** -* @brief -* Convert a value @v to a serialized string using serial implementation @impl -* -* @param[in] impl -* Reference to the serial implementation will be used to serialize value @v -* -* @param[in] v -* Reference to the value is going to be serialized -* -* @param[out] size -* Size in bytes of the return buffer -* -* @param[in] allocator -* Allocator to be used serialize @v -* -* @return -* String with the value serialized on correct serialization, null otherwise -* -*/ -SERIAL_API char * serial_impl_serialize(serial_impl impl, value v, size_t * size, memory_allocator allocator); - -/** -* @brief -* Convert a string @buffer to a deserialized value using serial implementation @impl -* -* @param[in] impl -* Reference to the serial implementation will be used to deserialize string @buffer -* -* @param[in] buffer -* Reference to the string is going to be deserialized -* -* @param[in] size -* Size in bytes of the string @buffer -* -* @param[in] allocator -* Allocator to be used deserialize @buffer -* -* @return -* Pointer to value deserialized on correct serialization, null otherwise -* -*/ -SERIAL_API value serial_impl_deserialize(serial_impl impl, const char * buffer, size_t size, memory_allocator allocator); - -/** -* @brief -* Unload serial implementation @impl -* -* @param[in] impl -* Pointer to the serial implementation to be unloaded -* -* @return -* Returns zero on correct unloading, distinct from zero otherwise -* -*/ -SERIAL_API int serial_impl_unload(serial_impl impl); - -/** -* @brief -* Destroy serial implementation -* -* @param[in] impl -* Pointer to the serial implementation to be destroyed -* -* @return -* Returns zero on correct destruction, distinct from zero otherwise -* -*/ -SERIAL_API int serial_impl_destroy(serial_impl impl); - -#ifdef __cplusplus -} -#endif - -#endif /* SERIAL_IMPL_H */ diff --git a/source/serial/include/serial/serial_impl_handle.h b/source/serial/include/serial/serial_impl_handle.h deleted file mode 100644 index a35c9bfca3..0000000000 --- a/source/serial/include/serial/serial_impl_handle.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Serial Library by Parra Studios - * A cross-platform library for managing multiple serialization and deserialization formats. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef SERIAL_IMPL_HANDLE_H -#define SERIAL_IMPL_HANDLE_H 1 - -/* -- Headers -- */ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Type Definitions -- */ - -typedef void * serial_impl_handle; - -#ifdef __cplusplus -} -#endif - -#endif /* SERIAL_IMPL_HANDLE_H */ diff --git a/source/serial/include/serial/serial_interface.h b/source/serial/include/serial/serial_interface.h index 538083e0dd..f83a0af8e8 100644 --- a/source/serial/include/serial/serial_interface.h +++ b/source/serial/include/serial/serial_interface.h @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -13,8 +13,7 @@ #include -#include -#include +#include #include @@ -24,37 +23,21 @@ extern "C" { #endif -/* -- Forward Declarations -- */ - -struct serial_interface_type; - -/* -- Type Definitions -- */ - -typedef const char * (*serial_interface_extension)(void); - -typedef serial_impl_handle (*serial_interface_initialize)(memory_allocator, serial_host); - -typedef char * (*serial_interface_serialize)(serial_impl_handle, value, size_t *); - -typedef value (*serial_interface_deserialize)(serial_impl_handle, const char *, size_t); - -typedef int (*serial_interface_destroy)(serial_impl_handle); - -typedef struct serial_interface_type * serial_interface; - -typedef serial_interface (*serial_interface_singleton)(void); - /* -- Member Data -- */ struct serial_interface_type { - serial_interface_extension extension; - serial_interface_initialize initialize; - serial_interface_serialize serialize; - serial_interface_deserialize deserialize; - serial_interface_destroy destroy; + const char *(*extension)(void); + serial_handle (*initialize)(memory_allocator); + char *(*serialize)(serial_handle, value, size_t *); + value (*deserialize)(serial_handle, const char *, size_t); + int (*destroy)(serial_handle); }; +/* -- Type Definitions -- */ + +typedef struct serial_interface_type *serial_interface; + #ifdef __cplusplus } #endif diff --git a/source/serial/include/serial/serial_singleton.h b/source/serial/include/serial/serial_singleton.h deleted file mode 100644 index ce80b83736..0000000000 --- a/source/serial/include/serial/serial_singleton.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * A cross-platform library for managing multiple serialization and deserialization formats. - * - */ - -#ifndef SERIAL_SINGLETON_H -#define SERIAL_SINGLETON_H 1 - -/* -- Headers -- */ - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Forward Declarations -- */ - -struct serial_singleton_type; - -/* -- Type Definitions -- */ - -typedef struct serial_singleton_type * serial_singleton; - -/* -- Methods -- */ - -/** -* @brief -* Initialize serial singleton -* -* @return -* Returns zero on correct serial singleton initialization, distinct from zero otherwise -* -*/ -SERIAL_API int serial_singleton_initialize(); - -/** -* @brief -* Register serial into serials map -* -* @param[in] s -* Pointer to serial -* -* @return -* Returns zero on correct serial singleton insertion, distinct from zero otherwise -* -*/ -SERIAL_API int serial_singleton_register(serial s); - -/** -* @brief -* Retrieve serial from serials map by @name -* -* @param[in] name -* Index which references the serial to be retrieved -* -* @return -* Returns pointer to serial if exists, null otherwise -* -*/ -SERIAL_API serial serial_singleton_get(const char * name); - -/** -* @brief -* Retrieve serial library path where serials are located -* -* @return -* Returns constant string representing serials library path -* -*/ -SERIAL_API const char * serial_singleton_path(void); - -/** -* @brief -* Remove serial from serials map -* -* @param[in] s -* Pointer to serial -* -* @return -* Returns zero on correct serial singleton removing, distinct from zero otherwise -* -*/ -SERIAL_API int serial_singleton_clear(serial s); - -/** -* @brief -* Destroy serial singleton -* -*/ -SERIAL_API void serial_singleton_destroy(void); - -#ifdef __cplusplus -} -#endif - -#endif /* SERIAL_SINGLETON_H */ diff --git a/source/serial/source/serial.c b/source/serial/source/serial.c index 2b052189d1..7a7e71faba 100644 --- a/source/serial/source/serial.c +++ b/source/serial/source/serial.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -11,225 +11,146 @@ #include #include -#include -#include +#include + +#include #include -#include +/* -- Definitions -- */ + +#define SERIAL_MANAGER_NAME "serial" +#define SERIAL_LIBRARY_PATH "SERIAL_LIBRARY_PATH" +#define SERIAL_LIBRARY_DEFAULT_PATH "serials" + +/* -- Macros -- */ + +#define serial_iface(s) \ + plugin_iface_type(s, serial_interface) /* -- Member Data -- */ -struct serial_type -{ - char * name; - serial_impl impl; -}; +static plugin_manager_declare(serial_manager); /* -- Methods -- */ -int serial_initialize() +int serial_initialize(void) { - if (serial_singleton_initialize() != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton initialization"); - - return 1; - } + return plugin_manager_initialize( + &serial_manager, + SERIAL_MANAGER_NAME, + SERIAL_LIBRARY_PATH, +#if defined(SERIAL_LIBRARY_INSTALL_PATH) + SERIAL_LIBRARY_INSTALL_PATH, +#else + SERIAL_LIBRARY_DEFAULT_PATH, +#endif /* SERIAL_LIBRARY_INSTALL_PATH */ + NULL, + NULL); +} - return 0; +serial serial_create(const char *name) +{ + return plugin_manager_create(&serial_manager, name, NULL, NULL); } -serial serial_create(const char * name) +const char *serial_extension(serial s) { - serial s; + return serial_iface(s)->extension(); +} - size_t name_length; +const char *serial_name(serial s) +{ + return plugin_name(s); +} - if (name == NULL) +char *serial_serialize(serial s, value v, size_t *size, memory_allocator allocator) +{ + if (s == NULL || v == NULL || size == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial name"); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serialization arguments"); return NULL; } - s = serial_singleton_get(name); - - if (s != NULL) - { - log_write("metacall", LOG_LEVEL_DEBUG, "Serial <%p> already exists", (void *)s); - - return s; - } - - name_length = strlen(name); + serial_handle handle = serial_iface(s)->initialize(allocator); - if (name_length == 0) + if (handle == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial name length"); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); return NULL; } - s = malloc(sizeof(struct serial_type)); + char *buffer = serial_iface(s)->serialize(handle, v, size); - if (s == NULL) + if (buffer == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial allocation"); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle serialization"); } - s->name = malloc(sizeof(char) * (name_length + 1)); - - if (s->name == NULL) + if (serial_iface(s)->destroy(handle) != 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial name allocation"); - - free(s); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); } - strncpy(s->name, name, name_length); - - s->name[name_length] = '\0'; - - s->impl = serial_impl_create(); + return buffer; +} - if (s->impl == NULL) +value serial_deserialize(serial s, const char *buffer, size_t size, memory_allocator allocator) +{ + if (s == NULL || buffer == NULL || size == 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation creation"); - - free(s->name); - - free(s); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid deserialization arguments"); return NULL; } - if (serial_impl_load(s->impl, serial_singleton_path(), s->name) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation loading"); - - serial_impl_destroy(s->impl); - - free(s->name); + serial_handle handle = serial_iface(s)->initialize(allocator); - free(s); - - return NULL; - } - - if (serial_singleton_register(s) != 0) + if (handle == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton insert"); - - serial_impl_destroy(s->impl); - - free(s->name); - - free(s); + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); return NULL; } - return s; -} + value v = serial_iface(s)->deserialize(handle, buffer, size); -const char * serial_extension(serial s) -{ - return serial_impl_extension(s->impl); -} - -const char * serial_name(serial s) -{ - return s->name; -} - -char * serial_serialize(serial s, value v, size_t * size, memory_allocator allocator) -{ - if (s == NULL || v == NULL || size == NULL) + if (v == NULL) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serialization arguments"); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle deserialization"); } - return serial_impl_serialize(s->impl, v, size, allocator); -} - -value serial_deserialize(serial s, const char * buffer, size_t size, memory_allocator allocator) -{ - if (s == NULL || buffer == NULL || size == 0) + if (serial_iface(s)->destroy(handle) != 0) { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid deserialization arguments"); - - return NULL; + log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); } - return serial_impl_deserialize(s->impl, buffer, size, allocator); + return v; } int serial_clear(serial s) { - if (s != NULL) - { - int result = 0; - - if (serial_singleton_clear(s) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton clear"); - - result = 1; - } - - if (s->name != NULL) - { - free(s->name); - } - - if (s->impl != NULL) - { - if (serial_impl_unload(s->impl) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation unloading"); - - result = 1; - } - - if (serial_impl_destroy(s->impl) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation destruction"); - - result = 1; - } - } - - free(s); - - return result; - } - - return 0; + return plugin_manager_clear(&serial_manager, s); } -void serial_destroy() +void serial_destroy(void) { - serial_singleton_destroy(); + plugin_manager_destroy(&serial_manager); } -const char * serial_print_info() +const char *serial_print_info(void) { static const char serial_info[] = "Serial Library " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef SERIAL_STATIC_DEFINE - "Compiled as static library type" - #else - "Compiled as shared library type" - #endif +#ifdef SERIAL_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif "\n"; diff --git a/source/serial/source/serial_impl.c b/source/serial/source/serial_impl.c deleted file mode 100644 index a9dc644375..0000000000 --- a/source/serial/source/serial_impl.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * A cross-platform library for managing multiple serialization and deserialization formats. - * - */ - -/* -- Headers -- */ - -#include -#include -#include - -#include - -#include - -#include - -/* -- Definitions -- */ - -#define SERIAL_DYNLINK_NAME_SIZE 0x40 -#define SERIAL_DYNLINK_SUFFIX "_serial" - -/* -- Member Data -- */ - -struct serial_impl_type -{ - dynlink handle; - serial_interface iface; - serial_host host; -}; - -/* -- Private Methods -- */ - -static dynlink serial_impl_load_dynlink(const char * path, const char * name); - -static int serial_impl_load_symbol(dynlink handle, const char * name, dynlink_symbol_addr * singleton_addr_ptr); - -/* -- Methods -- */ - -dynlink serial_impl_load_dynlink(const char * path, const char * name) -{ - #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) - const char serial_dynlink_suffix[] = SERIAL_DYNLINK_SUFFIX "d"; - #else - const char serial_dynlink_suffix[] = SERIAL_DYNLINK_SUFFIX; - #endif - - #define SERIAL_DYNLINK_NAME_FULL_SIZE \ - (sizeof(serial_dynlink_suffix) + SERIAL_DYNLINK_NAME_SIZE) - - char serial_dynlink_name[SERIAL_DYNLINK_NAME_FULL_SIZE]; - - strncpy(serial_dynlink_name, name, SERIAL_DYNLINK_NAME_FULL_SIZE); - - strncat(serial_dynlink_name, serial_dynlink_suffix, - SERIAL_DYNLINK_NAME_FULL_SIZE - strnlen(serial_dynlink_name, SERIAL_DYNLINK_NAME_FULL_SIZE - 1) - 1); - - #undef SERIAL_DYNLINK_NAME_FULL_SIZE - - log_write("metacall", LOG_LEVEL_DEBUG, "Loading serial plugin: %s", serial_dynlink_name); - - return dynlink_load(path, serial_dynlink_name, DYNLINK_FLAGS_BIND_LAZY | DYNLINK_FLAGS_BIND_GLOBAL); -} - -int serial_impl_load_symbol(dynlink handle, const char * name, dynlink_symbol_addr * singleton_addr_ptr) -{ - const char serial_dynlink_symbol_prefix[] = DYNLINK_SYMBOL_STR(""); - const char serial_dynlink_symbol_suffix[] = "_serial_impl_interface_singleton"; - - #define SERIAL_DYNLINK_SYMBOL_SIZE \ - (sizeof(serial_dynlink_symbol_prefix) + SERIAL_DYNLINK_NAME_SIZE + sizeof(serial_dynlink_symbol_suffix)) - - char serial_dynlink_symbol[SERIAL_DYNLINK_SYMBOL_SIZE]; - - strncpy(serial_dynlink_symbol, serial_dynlink_symbol_prefix, SERIAL_DYNLINK_SYMBOL_SIZE); - - strncat(serial_dynlink_symbol, name, - SERIAL_DYNLINK_SYMBOL_SIZE - strnlen(serial_dynlink_symbol, SERIAL_DYNLINK_SYMBOL_SIZE - 1) - 1); - - strncat(serial_dynlink_symbol, serial_dynlink_symbol_suffix, - SERIAL_DYNLINK_SYMBOL_SIZE - strnlen(serial_dynlink_symbol, SERIAL_DYNLINK_SYMBOL_SIZE - 1) - 1); - - #undef SERIAL_DYNLINK_SYMBOL_SIZE - - log_write("metacall", LOG_LEVEL_DEBUG, "Loading serial symbol: %s", serial_dynlink_symbol); - - return dynlink_symbol(handle, serial_dynlink_symbol, singleton_addr_ptr); -} - -serial_impl serial_impl_create() -{ - serial_impl impl = malloc(sizeof(struct serial_impl_type)); - - if (impl == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation allocation"); - - return NULL; - } - - impl->host = (serial_host)malloc(sizeof(struct serial_host_type)); - - if (impl->host == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation host allocation"); - - free(impl); - - return NULL; - } - - impl->host->log = log_instance(); - - impl->handle = NULL; - impl->iface = NULL; - - return impl; -} - -const char * serial_impl_extension(serial_impl impl) -{ - return impl->iface->extension(); -} - -int serial_impl_load(serial_impl impl, const char * path, const char * name) -{ - dynlink_symbol_addr singleton_addr; - - serial_interface_singleton iface_singleton; - - impl->handle = serial_impl_load_dynlink(path, name); - - if (impl->handle == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial dynlink loading"); - - return 1; - } - - if (serial_impl_load_symbol(impl->handle, name, &singleton_addr) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial dynlink symbol loading"); - - dynlink_unload(impl->handle); - - impl->handle = NULL; - - return 1; - } - - iface_singleton = (serial_interface_singleton)DYNLINK_SYMBOL_GET(singleton_addr); - - if (iface_singleton == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial interface singleton access"); - - dynlink_unload(impl->handle); - - impl->handle = NULL; - - return 1; - } - - impl->iface = iface_singleton(); - - return 0; -} - -char * serial_impl_serialize(serial_impl impl, value v, size_t * size, memory_allocator allocator) -{ - serial_impl_handle handle = impl->iface->initialize(allocator, impl->host); - - char * buffer; - - if (handle == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); - - return NULL; - } - - buffer = impl->iface->serialize(handle, v, size); - - if (buffer == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle serialization"); - } - - if (impl->iface->destroy(handle) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); - } - - return buffer; -} - -value serial_impl_deserialize(serial_impl impl, const char * buffer, size_t size, memory_allocator allocator) -{ - serial_impl_handle handle = impl->iface->initialize(allocator, impl->host); - - value v; - - if (handle == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle initialization"); - - return NULL; - } - - v = impl->iface->deserialize(handle, buffer, size); - - if (v == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle deserialization"); - } - - if (impl->iface->destroy(handle) != 0) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial implementation handle destruction"); - } - - return v; -} - -int serial_impl_unload(serial_impl impl) -{ - if (impl != NULL) - { - impl->iface = NULL; - - if (impl->handle != NULL) - { - dynlink_unload(impl->handle); - - impl->handle = NULL; - } - } - - return 0; -} - -int serial_impl_destroy(serial_impl impl) -{ - if (impl != NULL) - { - if (impl->handle != NULL) - { - dynlink_unload(impl->handle); - } - - if (impl->host != NULL) - { - free(impl->host); - } - - free(impl); - } - - return 0; -} diff --git a/source/serial/source/serial_singleton.c b/source/serial/source/serial_singleton.c deleted file mode 100644 index 41c70928dc..0000000000 --- a/source/serial/source/serial_singleton.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * A cross-platform library for managing multiple serialization and deserialization formats. - * - */ - -/* -- Headers -- */ - -#include - -#include - -#include - -#include - -#include - -/* -- Definitions -- */ - -#define SERIAL_LIBRARY_PATH "SERIAL_LIBRARY_PATH" -#define SERIAL_LIBRARY_DEFAULT_PATH "serials" - -/* -- Member Data -- */ - -struct serial_singleton_type -{ - set serials; - char * library_path; -}; - -/* -- Private Methods -- */ - -/** -* @brief -* Wrapper of serial singleton instance -* -* @return -* Pointer to serial singleton instance -* -*/ -static serial_singleton serial_singleton_instance(void); - -/** -* @brief -* Serial singleton destroy callback iterator -* -* @param[in] s -* Pointer to serials set -* -* @param[in] key -* Pointer to current serial key -* -* @param[in] val -* Pointer to current serial instance -* -* @param[in] args -* Pointer to user defined callback arguments -* -* @return -* Returns zero to continue iteration, distinct from zero otherwise -* -*/ -static int serial_singleton_destroy_cb_iterate(set s, set_key key, set_value val, set_cb_iterate_args args); - -/* -- Methods -- */ - -serial_singleton serial_singleton_instance() -{ - static struct serial_singleton_type singleton = - { - NULL, - NULL - }; - - return &singleton; -} - -int serial_singleton_initialize() -{ - serial_singleton singleton = serial_singleton_instance(); - - if (singleton->serials == NULL) - { - singleton->serials = set_create(&hash_callback_str, &comparable_callback_str); - - if (singleton->serials == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton set initialization"); - - serial_singleton_destroy(); - - return 1; - } - } - - if (singleton->library_path == NULL) - { - static const char serial_library_path[] = SERIAL_LIBRARY_PATH; - - #if defined(SERIAL_LIBRARY_INSTALL_PATH) - static const char serial_library_default_path[] = SERIAL_LIBRARY_INSTALL_PATH; - #else - static const char serial_library_default_path[] = SERIAL_LIBRARY_DEFAULT_PATH; - #endif /* SERIAL_LIBRARY_INSTALL_PATH */ - - singleton->library_path = environment_variable_path_create(serial_library_path, serial_library_default_path); - - if (singleton->library_path == NULL) - { - log_write("metacall", LOG_LEVEL_ERROR, "Invalid serial singleton library path initialization"); - - serial_singleton_destroy(); - - return 1; - } - } - - return 0; -} - -int serial_singleton_register(serial s) -{ - serial_singleton singleton = serial_singleton_instance(); - - const char * name = serial_name(s); - - if (set_get(singleton->serials, (set_key)name) != NULL) - { - return 1; - } - - return set_insert(singleton->serials, (set_key)name, s); -} - -serial serial_singleton_get(const char * name) -{ - serial_singleton singleton = serial_singleton_instance(); - - return set_get(singleton->serials, (set_key)name); -} - -const char * serial_singleton_path() -{ - serial_singleton singleton = serial_singleton_instance(); - - return singleton->library_path; -} - -int serial_singleton_clear(serial s) -{ - serial_singleton singleton = serial_singleton_instance(); - - const char * name = serial_name(s); - - if (set_get(singleton->serials, (set_key)name) == NULL) - { - return 0; - } - - if (set_remove(singleton->serials, (const set_key)name) == NULL) - { - return 1; - } - - return 0; -} - -int serial_singleton_destroy_cb_iterate(set s, set_key key, set_value val, set_cb_iterate_args args) -{ - (void)s; - (void)key; - (void)args; - - if (val != NULL) - { - serial_clear((serial)val); - } - - return 0; -} - -void serial_singleton_destroy() -{ - serial_singleton singleton = serial_singleton_instance(); - - if (singleton->serials != NULL) - { - set_iterate(singleton->serials, &serial_singleton_destroy_cb_iterate, NULL); - - set_destroy(singleton->serials); - - singleton->serials = NULL; - } - - if (singleton->library_path != NULL) - { - environment_variable_path_destroy(singleton->library_path); - - singleton->library_path = NULL; - } -} diff --git a/source/serials/CMakeLists.txt b/source/serials/CMakeLists.txt index 2c796d7368..5fc2711dc5 100644 --- a/source/serials/CMakeLists.txt +++ b/source/serials/CMakeLists.txt @@ -7,20 +7,6 @@ endif() option(OPTION_BUILD_SERIALS_METACALL "MetaCall Native Format library serial." ON) option(OPTION_BUILD_SERIALS_RAPID_JSON "RapidJSON library serial." ON) -# Define serial modules -set(SERIAL_MODULE_NAMES - version - preprocessor - environment - format - log - memory - adt - reflect - dynlink - serial -) - # Serial packages add_subdirectory(metacall_serial) # MetaCall Native Format library add_subdirectory(rapid_json_serial) # RapidJSON library diff --git a/source/serials/metacall_serial/CMakeLists.txt b/source/serials/metacall_serial/CMakeLists.txt index d833e9abe1..5a11cbc8e5 100644 --- a/source/serials/metacall_serial/CMakeLists.txt +++ b/source/serials/metacall_serial/CMakeLists.txt @@ -15,7 +15,6 @@ message(STATUS "Serial ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -64,54 +63,18 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Create library # -include(Distributable) - -# Create unity build library -set(metacall_serial_unity_build "${CMAKE_CURRENT_BINARY_DIR}/${target}_unity_build.c") - -distributable_generate(${target} ${metacall_serial_unity_build} - ${SERIAL_MODULE_NAMES} -) - # Build library add_library(${target} MODULE - ${metacall_serial_unity_build} ${sources} ${headers} ) -# Add target dependencies -add_dependencies(${target} - ${SERIAL_MODULE_NAMES} -) - # Create namespaced alias add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() - # Create API export header generate_export_header(${target} EXPORT_FILE_NAME ${export_file} @@ -139,6 +102,8 @@ target_include_directories(${target} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include + $ # MetaCall includes + PUBLIC ${DEFAULT_INCLUDE_DIRECTORIES} @@ -154,6 +119,7 @@ target_include_directories(${target} target_link_libraries(${target} PRIVATE + ${META_PROJECT_NAME}::metacall # MetaCall library PUBLIC ${DEFAULT_LIBRARIES} @@ -192,7 +158,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h index c07ebc41d6..d6aece4e79 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ #include -#include - #ifdef __cplusplus extern "C" { #endif @@ -45,8 +43,6 @@ extern "C" { */ METACALL_SERIAL_API serial_interface metacall_serial_impl_interface_singleton(void); -DYNLINK_SYMBOL_EXPORT(metacall_serial_impl_interface_singleton); - /** * @brief * Provide the module information @@ -55,9 +51,7 @@ DYNLINK_SYMBOL_EXPORT(metacall_serial_impl_interface_singleton); * Static string containing module information * */ -METACALL_SERIAL_API const char * metacall_serial_print_info(void); - -DYNLINK_SYMBOL_EXPORT(metacall_serial_print_info); +METACALL_SERIAL_API const char *metacall_serial_print_info(void); #ifdef __cplusplus } diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h index c89d9e3b21..8dd25ce64a 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl.h @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -29,7 +29,7 @@ extern "C" { * Returns constant string representing serial extension * */ -METACALL_SERIAL_API const char * metacall_serial_impl_extension(void); +METACALL_SERIAL_API const char *metacall_serial_impl_extension(void); /** * @brief @@ -39,7 +39,7 @@ METACALL_SERIAL_API const char * metacall_serial_impl_extension(void); * Returns pointer to serial document implementation on success, null pointer otherwise * */ -METACALL_SERIAL_API serial_impl_handle metacall_serial_impl_initialize(memory_allocator allocator, serial_host host); +METACALL_SERIAL_API serial_handle metacall_serial_impl_initialize(memory_allocator allocator); /** * @brief @@ -58,7 +58,7 @@ METACALL_SERIAL_API serial_impl_handle metacall_serial_impl_initialize(memory_al * String with the value serialized on correct serialization, null otherwise * */ -METACALL_SERIAL_API char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size); +METACALL_SERIAL_API char *metacall_serial_impl_serialize(serial_handle handle, value v, size_t *size); /** * @brief @@ -77,7 +77,7 @@ METACALL_SERIAL_API char * metacall_serial_impl_serialize(serial_impl_handle han * Pointer to value deserialized on correct serialization, null otherwise * */ -METACALL_SERIAL_API value metacall_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size); +METACALL_SERIAL_API value metacall_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size); /** * @brief @@ -87,7 +87,7 @@ METACALL_SERIAL_API value metacall_serial_impl_deserialize(serial_impl_handle ha * Returns zero on correct destruction, distinct from zero otherwise * */ -METACALL_SERIAL_API int metacall_serial_impl_destroy(serial_impl_handle handle); +METACALL_SERIAL_API int metacall_serial_impl_destroy(serial_handle handle); #ifdef __cplusplus } diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h index 964e19ce81..f6324b4412 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_deserialize.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h index ef74ceb20e..21c0eb0601 100644 --- a/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h +++ b/source/serials/metacall_serial/include/metacall_serial/metacall_serial_impl_serialize.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ typedef void (*metacall_serialize_impl_ptr)(value, char *, size_t, const char *, * @return * Print format assigned to type id @id */ -METACALL_SERIAL_API const char * metacall_serial_impl_serialize_format(type_id id); +METACALL_SERIAL_API const char *metacall_serial_impl_serialize_format(type_id id); /** * @brief diff --git a/source/serials/metacall_serial/source/metacall_serial.c b/source/serials/metacall_serial/source/metacall_serial.c index 024c82a08c..70b16af396 100644 --- a/source/serials/metacall_serial/source/metacall_serial.c +++ b/source/serials/metacall_serial/source/metacall_serial.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -15,10 +15,9 @@ /* -- Methods -- */ -serial_interface metacall_serial_impl_interface_singleton() +serial_interface metacall_serial_impl_interface_singleton(void) { - static struct serial_interface_type interface_instance_metacall = - { + static struct serial_interface_type interface_instance_metacall = { &metacall_serial_impl_extension, &metacall_serial_impl_initialize, &metacall_serial_impl_serialize, @@ -29,17 +28,17 @@ serial_interface metacall_serial_impl_interface_singleton() return &interface_instance_metacall; } -const char * metacall_serial_print_info() +const char *metacall_serial_print_info(void) { static const char metacall_serial_info[] = "MetaCall Native Format Serial Plugin " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef METACALL_SERIAL_STATIC_DEFINE - "Compiled as static library type\n" - #else - "Compiled as shared library type\n" - #endif +#ifdef METACALL_SERIAL_STATIC_DEFINE + "Compiled as static library type\n" +#else + "Compiled as shared library type\n" +#endif "\n"; diff --git a/source/serials/metacall_serial/source/metacall_serial_impl.c b/source/serials/metacall_serial/source/metacall_serial_impl.c index 8ecf9b68bd..8e48d1e7d5 100644 --- a/source/serials/metacall_serial/source/metacall_serial_impl.c +++ b/source/serials/metacall_serial/source/metacall_serial_impl.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -9,51 +9,49 @@ /* -- Headers -- */ #include -#include #include +#include #include /* -- Private Methods -- */ -static void metacall_serial_impl_serialize_value(value v, char * dest, size_t size, size_t * length); +static void metacall_serial_impl_serialize_value(value v, char *dest, size_t size, size_t *length); -static value metacall_serial_impl_deserialize_value(const char * buffer, size_t size); +static value metacall_serial_impl_deserialize_value(const char *buffer, size_t size); /* -- Methods -- */ -const char * metacall_serial_impl_extension() +const char *metacall_serial_impl_extension(void) { static const char extension[] = "meta"; return extension; } -serial_impl_handle metacall_serial_impl_initialize(memory_allocator allocator, serial_host host) +serial_handle metacall_serial_impl_initialize(memory_allocator allocator) { - log_copy(host->log); - return allocator; } -void metacall_serial_impl_serialize_value(value v, char * dest, size_t size, size_t * length) +void metacall_serial_impl_serialize_value(value v, char *dest, size_t size, size_t *length) { type_id id = value_type_id(v); - const char * format = metacall_serial_impl_serialize_format(id); + const char *format = metacall_serial_impl_serialize_format(id); metacall_serialize_impl_ptr serialize_ptr = metacall_serial_impl_serialize_func(id); serialize_ptr(v, dest, size, format, length); } -char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size) +char *metacall_serial_impl_serialize(serial_handle handle, value v, size_t *size) { memory_allocator allocator; size_t length, buffer_size; - char * buffer; + char *buffer; if (handle == NULL || v == NULL || size == NULL) { @@ -91,8 +89,9 @@ char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t if (length + 1 != buffer_size) { log_write("metacall", LOG_LEVEL_ERROR, "Serialization invalid length + 1 != buffer " - "(%" PRIuS " != %" PRIuS ") in MetaCall Native Format implementation", length + 1, buffer_size); - + "(%" PRIuS " != %" PRIuS ") in MetaCall Native Format implementation", + length + 1, buffer_size); + memory_allocator_deallocate(allocator, buffer); *size = 0; @@ -105,7 +104,7 @@ char * metacall_serial_impl_serialize(serial_impl_handle handle, value v, size_t return buffer; } -value metacall_serial_impl_deserialize_value(const char * buffer, size_t size) +value metacall_serial_impl_deserialize_value(const char *buffer, size_t size) { value v = NULL; @@ -126,7 +125,7 @@ value metacall_serial_impl_deserialize_value(const char * buffer, size_t size) return NULL; } -value metacall_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size) +value metacall_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size) { if (handle == NULL || buffer == NULL || size == 0) { @@ -138,7 +137,7 @@ value metacall_serial_impl_deserialize(serial_impl_handle handle, const char * b return metacall_serial_impl_deserialize_value(buffer, size); } -int metacall_serial_impl_destroy(serial_impl_handle handle) +int metacall_serial_impl_destroy(serial_handle handle) { (void)handle; diff --git a/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c b/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c index c82d34a821..3f415ade35 100644 --- a/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c +++ b/source/serials/metacall_serial/source/metacall_serial_impl_deserialize.c @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,43 +24,42 @@ #include -#include #include +#include /* -- Private Methods -- */ -static int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_bool(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_char(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_char(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_short(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_short(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_int(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_int(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_long(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_long(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_float(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_float(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_double(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_double(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_string(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_string(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_buffer(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_buffer(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_array(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_array(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_map(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_map(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_ptr(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_ptr(value *v, const char *src, size_t length); -static int metacall_serial_impl_deserialize_null(value * v, const char * src, size_t length); +static int metacall_serial_impl_deserialize_null(value *v, const char *src, size_t length); /* -- Methods -- */ metacall_deserialize_impl_ptr metacall_serial_impl_deserialize_func(type_id id) { - static metacall_deserialize_impl_ptr deserialize_func[TYPE_SIZE] = - { + static metacall_deserialize_impl_ptr deserialize_func[TYPE_SIZE] = { &metacall_serial_impl_deserialize_bool, &metacall_serial_impl_deserialize_char, &metacall_serial_impl_deserialize_short, @@ -79,7 +78,7 @@ metacall_deserialize_impl_ptr metacall_serial_impl_deserialize_func(type_id id) return deserialize_func[id]; } -int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_bool(value *v, const char *src, size_t length) { static const char true_str[] = "true"; @@ -91,7 +90,7 @@ int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t le return (*v == NULL); } - + if (strncmp(src, false_str, length) == 0) { *v = value_create_bool((boolean)0L); @@ -102,7 +101,7 @@ int metacall_serial_impl_deserialize_bool(value * v, const char * src, size_t le return 1; } -int metacall_serial_impl_deserialize_char(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_char(value *v, const char *src, size_t length) { long l = 0; @@ -125,7 +124,7 @@ int metacall_serial_impl_deserialize_char(value * v, const char * src, size_t le return (*v == NULL); } -int metacall_serial_impl_deserialize_short(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_short(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -135,7 +134,7 @@ int metacall_serial_impl_deserialize_short(value * v, const char * src, size_t l return 1; } -int metacall_serial_impl_deserialize_int(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_int(value *v, const char *src, size_t length) { size_t iterator; @@ -157,13 +156,13 @@ int metacall_serial_impl_deserialize_int(value * v, const char * src, size_t len return (*v == NULL); } -int metacall_serial_impl_deserialize_long(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_long(value *v, const char *src, size_t length) { char buffer[24]; const size_t last = length - 1; - char * end = (char *)&src[last]; + char *end = (char *)&src[last]; size_t iterator; @@ -194,7 +193,7 @@ int metacall_serial_impl_deserialize_long(value * v, const char * src, size_t le return (*v == NULL); } -int metacall_serial_impl_deserialize_float(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_float(value *v, const char *src, size_t length) { size_t iterator, is_digit; @@ -228,7 +227,7 @@ int metacall_serial_impl_deserialize_float(value * v, const char * src, size_t l return (*v == NULL); } -int metacall_serial_impl_deserialize_double(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_double(value *v, const char *src, size_t length) { size_t iterator, is_digit; @@ -260,7 +259,7 @@ int metacall_serial_impl_deserialize_double(value * v, const char * src, size_t return (*v == NULL); } -int metacall_serial_impl_deserialize_string(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_string(value *v, const char *src, size_t length) { size_t iterator; @@ -277,7 +276,7 @@ int metacall_serial_impl_deserialize_string(value * v, const char * src, size_t return (*v == NULL); } -int metacall_serial_impl_deserialize_buffer(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_buffer(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -287,7 +286,7 @@ int metacall_serial_impl_deserialize_buffer(value * v, const char * src, size_t return 1; } -int metacall_serial_impl_deserialize_array(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_array(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -297,7 +296,7 @@ int metacall_serial_impl_deserialize_array(value * v, const char * src, size_t l return 1; } -int metacall_serial_impl_deserialize_map(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_map(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -307,7 +306,7 @@ int metacall_serial_impl_deserialize_map(value * v, const char * src, size_t len return 1; } -int metacall_serial_impl_deserialize_ptr(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_ptr(value *v, const char *src, size_t length) { /* TODO */ (void)v; @@ -317,7 +316,7 @@ int metacall_serial_impl_deserialize_ptr(value * v, const char * src, size_t len return 1; } -int metacall_serial_impl_deserialize_null(value * v, const char * src, size_t length) +int metacall_serial_impl_deserialize_null(value *v, const char *src, size_t length) { static const char null_str[] = "(null)"; diff --git a/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c b/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c index 0ba086ef6f..16dd1d2679 100644 --- a/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c +++ b/source/serials/metacall_serial/source/metacall_serial_impl_serialize.c @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ #include +#include + #include #include @@ -29,88 +31,118 @@ /* -- Definitions -- */ #if defined(_WIN32) && defined(_MSC_VER) -# define METACALL_SERIALIZE_VALUE_FORMAT_PTR "0x%p" + #define METACALL_SERIALIZE_VALUE_FORMAT_PTR "0x%p" #elif defined(__linux) || defined(__linux__) -# define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" + #define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" #else -# define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" + #define METACALL_SERIALIZE_VALUE_FORMAT_PTR "%p" #endif /* -- Private Methods -- */ -static void metacall_serial_impl_serialize_bool(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_bool(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_char(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_short(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_int(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_long(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_float(value v, char *dest, size_t size, const char *format, size_t *length); + +static void metacall_serial_impl_serialize_double(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_char(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_string(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_short(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_buffer(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_int(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_array(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_long(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_map(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_float(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_ptr(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_double(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_future(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_string(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_function(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_null(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_class(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_map(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_object(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_ptr(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_exception(value v, char *dest, size_t size, const char *format, size_t *length); -static void metacall_serial_impl_serialize_null(value v, char * dest, size_t size, const char * format, size_t * length); +static void metacall_serial_impl_serialize_throwable(value v, char *dest, size_t size, const char *format, size_t *length); + +/* -- Definitions -- */ + +static const char *metacall_serialize_format[] = { + "%s", + "%c", + "%d", + "%d", + "%ld", + "%.6ff", + "%.6f", + "%s", + "%02x", + NULL, /* Unused */ + NULL, /* Unused */ + METACALL_SERIALIZE_VALUE_FORMAT_PTR, + NULL, /* TODO: Future */ + NULL, /* TODO: Function */ + "%s", + NULL, /* TODO: Class */ + NULL, /* TODO: Object */ + NULL, /* TODO: Exception */ + NULL /* TODO: Throwable */ +}; + +portability_static_assert((size_t)TYPE_SIZE == (size_t)sizeof(metacall_serialize_format) / sizeof(metacall_serialize_format[0]), + "MetaCall serializer format does not match MetaCall type size"); + +static metacall_serialize_impl_ptr serialize_func[] = { + &metacall_serial_impl_serialize_bool, + &metacall_serial_impl_serialize_char, + &metacall_serial_impl_serialize_short, + &metacall_serial_impl_serialize_int, + &metacall_serial_impl_serialize_long, + &metacall_serial_impl_serialize_float, + &metacall_serial_impl_serialize_double, + &metacall_serial_impl_serialize_string, + &metacall_serial_impl_serialize_buffer, + &metacall_serial_impl_serialize_array, + &metacall_serial_impl_serialize_map, + &metacall_serial_impl_serialize_ptr, + &metacall_serial_impl_serialize_future, + &metacall_serial_impl_serialize_function, + &metacall_serial_impl_serialize_null, + &metacall_serial_impl_serialize_class, + &metacall_serial_impl_serialize_object, + &metacall_serial_impl_serialize_exception, + &metacall_serial_impl_serialize_throwable +}; + +portability_static_assert((size_t)TYPE_SIZE == (size_t)sizeof(serialize_func) / sizeof(serialize_func[0]), + "MetaCall serializer function does not match MetaCall type size"); /* -- Methods -- */ -const char * metacall_serial_impl_serialize_format(type_id id) +const char *metacall_serial_impl_serialize_format(type_id id) { - static const char * metacall_serialize_format[TYPE_SIZE] = - { - "%s", - "%c", - "%d", - "%d", - "%ld", - "%.6ff", - "%.6f", - "%s", - "%02x", - NULL, /* Unused */ - NULL, /* Unused */ - METACALL_SERIALIZE_VALUE_FORMAT_PTR, - "%s" - }; - return metacall_serialize_format[id]; } metacall_serialize_impl_ptr metacall_serial_impl_serialize_func(type_id id) { - static metacall_serialize_impl_ptr serialize_func[TYPE_SIZE] = - { - &metacall_serial_impl_serialize_bool, - &metacall_serial_impl_serialize_char, - &metacall_serial_impl_serialize_short, - &metacall_serial_impl_serialize_int, - &metacall_serial_impl_serialize_long, - &metacall_serial_impl_serialize_float, - &metacall_serial_impl_serialize_double, - &metacall_serial_impl_serialize_string, - &metacall_serial_impl_serialize_buffer, - &metacall_serial_impl_serialize_array, - &metacall_serial_impl_serialize_map, - &metacall_serial_impl_serialize_ptr, - &metacall_serial_impl_serialize_null - }; - return serialize_func[id]; } -void metacall_serial_impl_serialize_bool(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_bool(value v, char *dest, size_t size, const char *format, size_t *length) { static const char value_boolean_str[] = "false\0true"; @@ -124,42 +156,42 @@ void metacall_serial_impl_serialize_bool(value v, char * dest, size_t size, cons *length = snprintf(dest, size, format, (const char *)(&value_boolean_str[offset])); } -void metacall_serial_impl_serialize_char(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_char(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_char(v)); } -void metacall_serial_impl_serialize_short(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_short(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_short(v)); } -void metacall_serial_impl_serialize_int(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_int(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_int(v)); } -void metacall_serial_impl_serialize_long(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_long(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_long(v)); } -void metacall_serial_impl_serialize_float(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_float(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_float(v)); } -void metacall_serial_impl_serialize_double(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_double(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_double(v)); } -void metacall_serial_impl_serialize_string(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_string(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_string(v)); } -void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_buffer(value v, char *dest, size_t size, const char *format, size_t *length) { if (dest == NULL && size == 0) { @@ -168,7 +200,7 @@ void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, co } else { - const char * buffer = (const char *)value_to_buffer(v); + const char *buffer = (const char *)value_to_buffer(v); size_t dest_iterator, iterator, buffer_length = 0, buffer_size = value_type_size(v); @@ -181,22 +213,22 @@ void metacall_serial_impl_serialize_buffer(value v, char * dest, size_t size, co } } -void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_array(value v, char *dest, size_t size, const char *format, size_t *length) { size_t iterator, array_value_length = 0, array_size = value_type_count(v); - value * array_value = value_to_array(v); + value *array_value = value_to_array(v); (void)format; - /* Calculate sum of all array values lenght */ + /* Calculate sum of all array values length */ for (iterator = 0; iterator < array_size; ++iterator) { value current_value = array_value[iterator]; type_id id = value_type_id(current_value); - const char * fmt = metacall_serial_impl_serialize_format(id); + const char *fmt = metacall_serial_impl_serialize_format(id); metacall_serialize_impl_ptr serialize_ptr = metacall_serial_impl_serialize_func(id); @@ -237,7 +269,7 @@ void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, con type_id id = value_type_id(current_value); - const char * fmt = metacall_serial_impl_serialize_format(id); + const char *fmt = metacall_serial_impl_serialize_format(id); metacall_serialize_impl_ptr serialize_ptr = metacall_serial_impl_serialize_func(id); @@ -266,19 +298,41 @@ void metacall_serial_impl_serialize_array(value v, char * dest, size_t size, con } } -void metacall_serial_impl_serialize_map(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_map(value v, char *dest, size_t size, const char *format, size_t *length) { /* TODO: Implement map iteration like array stringify */ *length = snprintf(dest, size, format, value_to_map(v)); } -void metacall_serial_impl_serialize_ptr(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_ptr(value v, char *dest, size_t size, const char *format, size_t *length) { *length = snprintf(dest, size, format, value_to_ptr(v)); } -void metacall_serial_impl_serialize_null(value v, char * dest, size_t size, const char * format, size_t * length) +void metacall_serial_impl_serialize_future(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement future serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_function(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement function serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_null(value v, char *dest, size_t size, const char *format, size_t *length) { static const char value_null_str[] = "(null)"; @@ -286,3 +340,47 @@ void metacall_serial_impl_serialize_null(value v, char * dest, size_t size, cons *length = snprintf(dest, size, format, value_null_str); } + +void metacall_serial_impl_serialize_class(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement class serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_object(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement object serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_exception(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement exception serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} + +void metacall_serial_impl_serialize_throwable(value v, char *dest, size_t size, const char *format, size_t *length) +{ + /* TODO: Implement throwable serialization */ + (void)v; + (void)dest; + (void)size; + (void)format; + + *length = 0; +} diff --git a/source/serials/rapid_json_serial/CMakeLists.txt b/source/serials/rapid_json_serial/CMakeLists.txt index 156b3ea690..29fd739aa5 100644 --- a/source/serials/rapid_json_serial/CMakeLists.txt +++ b/source/serials/rapid_json_serial/CMakeLists.txt @@ -8,13 +8,13 @@ endif() # External dependencies # -find_package(RapidJSON 1.1.0) +find_package(RapidJSON) if(NOT RAPIDJSON_FOUND) include(InstallRapidJSON) if(NOT RAPIDJSON_FOUND) - message(STATUS "RapidJSON libraries not found") + message(SEND_ERROR "RapidJSON libraries not found") return() endif() @@ -33,7 +33,6 @@ message(STATUS "Serial ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -78,18 +77,8 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Create library # -include(Distributable) - -# Create unity build library -set(rapid_json_serial_unity_build "${CMAKE_CURRENT_BINARY_DIR}/${target}_unity_build.c") - -distributable_generate(${target} ${rapid_json_serial_unity_build} - ${SERIAL_MODULE_NAMES} -) - # Build library add_library(${target} MODULE - ${rapid_json_serial_unity_build} ${sources} ${headers} ) @@ -108,7 +97,6 @@ endif() # Add target dependencies add_dependencies(${target} - ${SERIAL_MODULE_NAMES} RapidJSON ) @@ -118,27 +106,6 @@ add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() - # Create API export header generate_export_header(${target} EXPORT_FILE_NAME ${export_file} @@ -167,6 +134,7 @@ target_include_directories(${target} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include + $ # MetaCall includes ${RAPIDJSON_INCLUDE_DIRS} # RapidJSON includes PUBLIC @@ -184,6 +152,7 @@ target_include_directories(${target} target_link_libraries(${target} PRIVATE + ${META_PROJECT_NAME}::metacall # MetaCall library PUBLIC ${DEFAULT_LIBRARIES} @@ -222,7 +191,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC diff --git a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h index 73f676a69e..12bfd53432 100644 --- a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h +++ b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ #include -#include - #ifdef __cplusplus extern "C" { #endif @@ -45,8 +43,6 @@ extern "C" { */ RAPID_JSON_SERIAL_API serial_interface rapid_json_serial_impl_interface_singleton(void); -DYNLINK_SYMBOL_EXPORT(rapid_json_serial_impl_interface_singleton); - /** * @brief * Provide the module information @@ -55,9 +51,7 @@ DYNLINK_SYMBOL_EXPORT(rapid_json_serial_impl_interface_singleton); * Static string containing module information * */ -RAPID_JSON_SERIAL_API const char * rapid_json_serial_print_info(void); - -DYNLINK_SYMBOL_EXPORT(rapid_json_serial_print_info); +RAPID_JSON_SERIAL_API const char *rapid_json_serial_print_info(void); #ifdef __cplusplus } diff --git a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h index 20b5a79fd9..7d7fda83e7 100644 --- a/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h +++ b/source/serials/rapid_json_serial/include/rapid_json_serial/rapid_json_serial_impl.h @@ -2,7 +2,7 @@ * Serial Library by Parra Studios * A cross-platform library for managing multiple serialization and deserialization formats. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ extern "C" { * Returns constant string representing serial extension * */ -RAPID_JSON_SERIAL_API const char * rapid_json_serial_impl_extension(void); +RAPID_JSON_SERIAL_API const char *rapid_json_serial_impl_extension(void); /** * @brief @@ -51,7 +51,7 @@ RAPID_JSON_SERIAL_API const char * rapid_json_serial_impl_extension(void); * Returns pointer to serial document implementation on success, null pointer otherwise * */ -RAPID_JSON_SERIAL_API serial_impl_handle rapid_json_serial_impl_initialize(memory_allocator allocator, serial_host host); +RAPID_JSON_SERIAL_API serial_handle rapid_json_serial_impl_initialize(memory_allocator allocator); /** * @brief @@ -70,7 +70,7 @@ RAPID_JSON_SERIAL_API serial_impl_handle rapid_json_serial_impl_initialize(memor * String with the value serialized on correct serialization, null otherwise * */ -RAPID_JSON_SERIAL_API char * rapid_json_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size); +RAPID_JSON_SERIAL_API char *rapid_json_serial_impl_serialize(serial_handle handle, value v, size_t *size); /** * @brief @@ -89,7 +89,7 @@ RAPID_JSON_SERIAL_API char * rapid_json_serial_impl_serialize(serial_impl_handle * Pointer to value deserialized on correct serialization, null otherwise * */ -RAPID_JSON_SERIAL_API value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size); +RAPID_JSON_SERIAL_API value rapid_json_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size); /** * @brief @@ -99,7 +99,7 @@ RAPID_JSON_SERIAL_API value rapid_json_serial_impl_deserialize(serial_impl_handl * Returns zero on correct destruction, distinct from zero otherwise * */ -RAPID_JSON_SERIAL_API int rapid_json_serial_impl_destroy(serial_impl_handle handle); +RAPID_JSON_SERIAL_API int rapid_json_serial_impl_destroy(serial_handle handle); #ifdef __cplusplus } diff --git a/source/serials/rapid_json_serial/source/rapid_json_serial.c b/source/serials/rapid_json_serial/source/rapid_json_serial.c index c23b83016d..e7671b55a0 100644 --- a/source/serials/rapid_json_serial/source/rapid_json_serial.c +++ b/source/serials/rapid_json_serial/source/rapid_json_serial.c @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -15,10 +15,9 @@ /* -- Methods -- */ -serial_interface rapid_json_serial_impl_interface_singleton() +serial_interface rapid_json_serial_impl_interface_singleton(void) { - static struct serial_interface_type interface_instance_rapid_json = - { + static struct serial_interface_type interface_instance_rapid_json = { &rapid_json_serial_impl_extension, &rapid_json_serial_impl_initialize, &rapid_json_serial_impl_serialize, @@ -29,17 +28,17 @@ serial_interface rapid_json_serial_impl_interface_singleton() return &interface_instance_rapid_json; } -const char * rapid_json_serial_print_info() +const char *rapid_json_serial_print_info(void) { static const char rapid_json_serial_info[] = "Rapid JSON Serial Plugin " METACALL_VERSION "\n" - "Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia \n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" - #ifdef RAPID_JSON_SERIAL_STATIC_DEFINE - "Compiled as static library type\n" - #else - "Compiled as shared library type\n" - #endif +#ifdef RAPID_JSON_SERIAL_STATIC_DEFINE + "Compiled as static library type\n" +#else + "Compiled as shared library type\n" +#endif "\n"; diff --git a/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp b/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp index 9bbf0bd87a..f46cb09b1b 100644 --- a/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp +++ b/source/serials/rapid_json_serial/source/rapid_json_serial_impl.cpp @@ -1,6 +1,6 @@ /* * Serial Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A cross-platform library for managing multiple serialization and deserialization formats. * @@ -12,146 +12,74 @@ #include +/* Disable warnings from RapidJSON */ +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wstrict-overflow" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif + #include +#include #include #include -#include - -#include -/* -- Forward Declarations -- */ +/* Disable warnings from RapidJSON */ +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif -class MemoryAllocator; +#include /* -- Type Definitions -- */ -typedef rapidjson::GenericDocument, MemoryAllocator> RapidJSONSerialDocument; +typedef struct rapid_json_document_type +{ + rapidjson::Document impl; + memory_allocator allocator; -typedef rapidjson::GenericValue, MemoryAllocator> RapidJSONSerialValue; +} * rapid_json_document; /* -- Private Methods -- */ -static void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json_v, RapidJSONSerialDocument::AllocatorType & allocator); +static void rapid_json_serial_impl_serialize_value(value v, rapidjson::Value *json_v); -static char * rapid_json_serial_impl_document_stringify(RapidJSONSerialDocument * document, size_t * size); +static char *rapid_json_serial_impl_document_stringify(rapid_json_document document, size_t *size); -static value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v); +static value rapid_json_serial_impl_deserialize_value(const rapidjson::Value *v); /* -- Classes -- */ -/** -* @brief -* Memory allocator concept to decouple document allocation. -* Head like approach with each allocator has been implemented -* in order to support static like Free method used in RapidJSON -*/ -class MemoryAllocator -{ -public: - static const bool kNeedFree = true; - - MemoryAllocator() - { - this->allocator = memory_allocator_std(&malloc, &realloc, &free); - } - - MemoryAllocator(memory_allocator allocator) : allocator(allocator) - { - - } - - memory_allocator Impl() - { - return this->allocator; - } - - void * Malloc(size_t size) - { - void * data; - - if (size == 0) - { - return NULL; - } - - data = memory_allocator_allocate(allocator, sizeof(memory_allocator) + size); - - memcpy(data, &allocator, sizeof(memory_allocator)); - - return reinterpret_cast(reinterpret_cast(data) + sizeof(memory_allocator)); - } - - void * Realloc(void * data, size_t size, size_t new_size) - { - void * data_ptr, * new_data; - - (void)size; - - if (data == NULL) - { - return this->Malloc(new_size); - } - - data_ptr = reinterpret_cast(reinterpret_cast(data) - sizeof(memory_allocator)); - - new_data = memory_allocator_reallocate(allocator, data_ptr, size, new_size); - - return reinterpret_cast(reinterpret_cast(new_data) + sizeof(memory_allocator)); - } - - static void Free(void * data) - { - if (data != NULL) - { - void * data_ptr = reinterpret_cast(reinterpret_cast(data)-sizeof(memory_allocator)); - - memory_allocator allocator = *(static_cast(data_ptr)); - - memory_allocator_deallocate(allocator, data_ptr); - } - } - -private: - memory_allocator allocator; -}; +rapidjson::MemoryPoolAllocator<> rapid_json_allocator; /* -- Methods -- */ -const char * rapid_json_serial_impl_extension() +const char *rapid_json_serial_impl_extension() { static const char extension[] = "json"; return extension; } -serial_impl_handle rapid_json_serial_impl_initialize(memory_allocator allocator, serial_host host) +serial_handle rapid_json_serial_impl_initialize(memory_allocator allocator) { - MemoryAllocator * rapid_json_allocator; - - RapidJSONSerialDocument * document; - - log_copy(host->log); - - rapid_json_allocator = new MemoryAllocator(allocator); - - if (rapid_json_allocator == nullptr) - { - return NULL; - } - - document = new RapidJSONSerialDocument(rapid_json_allocator); + rapid_json_document document = new rapid_json_document_type(); if (document == nullptr) { - delete rapid_json_allocator; - return NULL; } - return (serial_impl_handle)document; + document->allocator = allocator; + + return (serial_handle)document; } -void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json_v, RapidJSONSerialDocument::AllocatorType & allocator) +void rapid_json_serial_impl_serialize_value(value v, rapidjson::Value *json_v) { type_id id = value_type_id(v); @@ -175,9 +103,7 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { short s = value_to_short(v); - int i = (int)s; - - json_v->SetInt(i); + json_v->SetInt((int)s); } else if (id == TYPE_INT) { @@ -189,9 +115,7 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { long l = value_to_long(v); - log_write("metacall", LOG_LEVEL_WARNING, "Casting long to int64_t (posible incompatible types) in RapidJSON implementation"); - - json_v->SetInt64(l); + json_v->SetInt64((int64_t)l); } else if (id == TYPE_FLOAT) { @@ -207,60 +131,60 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json } else if (id == TYPE_STRING) { - const char * str = value_to_string(v); + const char *str = value_to_string(v); size_t size = value_type_size(v); - rapidjson::SizeType length = size > 0 ? (rapidjson::SizeType)(size - 1) : 0; + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; json_v->SetString(str, length); } else if (id == TYPE_BUFFER) { - RapidJSONSerialValue & json_map = json_v->SetObject(); + rapidjson::Value &json_map = json_v->SetObject(); - RapidJSONSerialValue json_array(rapidjson::kArrayType); + rapidjson::Value json_array(rapidjson::kArrayType); - void * buffer = value_to_buffer(v); + void *buffer = value_to_buffer(v); size_t size = value_type_size(v); for (size_t iterator = 0; iterator < size; ++iterator) { - const char * data = (const char *)(((uintptr_t)buffer) + iterator); + const char *data = (const char *)(((uintptr_t)buffer) + iterator); - RapidJSONSerialValue json_inner_value; + rapidjson::Value json_inner_value; json_inner_value.SetUint((unsigned int)*data); - json_array.PushBack(json_inner_value, allocator); + json_array.PushBack(json_inner_value, rapid_json_allocator); } // Set data { - RapidJSONSerialValue json_member; + rapidjson::Value json_member; json_member.SetString("data"); - json_map.AddMember(json_member, json_array, allocator); + json_map.AddMember(json_member, json_array, rapid_json_allocator); } // Set length { - RapidJSONSerialValue json_member, json_inner_value; + rapidjson::Value json_member, json_inner_value; json_member.SetString("length"); json_inner_value.SetUint64((uint64_t)size); - json_map.AddMember(json_member, json_inner_value, allocator); + json_map.AddMember(json_member, json_inner_value, rapid_json_allocator); } } else if (id == TYPE_ARRAY) { - RapidJSONSerialValue & json_array = json_v->SetArray(); + rapidjson::Value &json_array = json_v->SetArray(); - value * value_array = value_to_array(v); + value *value_array = value_to_array(v); size_t array_size = value_type_count(v); @@ -268,18 +192,18 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { value current_value = value_array[iterator]; - RapidJSONSerialValue json_inner_value; + rapidjson::Value json_inner_value; - rapid_json_serial_impl_serialize_value(current_value, &json_inner_value, allocator); + rapid_json_serial_impl_serialize_value(current_value, &json_inner_value); - json_array.PushBack(json_inner_value, allocator); + json_array.PushBack(json_inner_value, rapid_json_allocator); } } else if (id == TYPE_MAP) { - RapidJSONSerialValue & json_map = json_v->SetObject(); + rapidjson::Value &json_map = json_v->SetObject(); - value * value_map = value_to_map(v); + value *value_map = value_to_map(v); size_t map_size = value_type_count(v); @@ -287,17 +211,115 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json { value tupla = value_map[iterator]; - value * tupla_array = value_to_array(tupla); + value *tupla_array = value_to_array(tupla); - RapidJSONSerialValue json_member, json_inner_value; + rapidjson::Value json_member, json_inner_value; - rapid_json_serial_impl_serialize_value(tupla_array[0], &json_member, allocator); + rapid_json_serial_impl_serialize_value(tupla_array[0], &json_member); - rapid_json_serial_impl_serialize_value(tupla_array[1], &json_inner_value, allocator); + rapid_json_serial_impl_serialize_value(tupla_array[1], &json_inner_value); - json_map.AddMember(json_member, json_inner_value, allocator); + json_map.AddMember(json_member, json_inner_value, rapid_json_allocator); } } + else if (id == TYPE_FUTURE) + { + /* TODO: Improve future serialization */ + static const char str[] = "[Future]"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; + + json_v->SetString(str, length); + } + else if (id == TYPE_FUNCTION) + { + /* TODO: Improve function serialization */ + static const char str[] = "[Function]"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; + + json_v->SetString(str, length); + } + else if (id == TYPE_CLASS) + { + /* TODO: Improve class serialization */ + static const char str[] = "[Class]"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; + + json_v->SetString(str, length); + } + else if (id == TYPE_OBJECT) + { + /* TODO: Improve object serialization */ + static const char str[] = "[Object]"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = size > 0 ? static_cast(size - 1) : 0; + + json_v->SetString(str, length); + } + else if (id == TYPE_EXCEPTION) + { + rapidjson::Value &json_map = json_v->SetObject(); + + exception ex = value_to_exception(v); + + rapidjson::Value message_member, message_value; + static const char message_str[] = "message"; + + message_member.SetString(message_str, static_cast(sizeof(message_str) - 1)); + message_value.SetString(exception_message(ex), static_cast(strlen(exception_message(ex)))); + json_map.AddMember(message_member, message_value, rapid_json_allocator); + + rapidjson::Value label_member, label_value; + static const char label_str[] = "label"; + + label_member.SetString(label_str, static_cast(sizeof(label_str) - 1)); + label_value.SetString(exception_label(ex), static_cast(strlen(exception_label(ex)))); + json_map.AddMember(label_member, label_value, rapid_json_allocator); + + rapidjson::Value code_member, code_value; + static const char code_str[] = "code"; + + code_member.SetString(code_str, static_cast(sizeof(code_str) - 1)); + code_value.SetInt64(exception_error_code(ex)); + json_map.AddMember(code_member, code_value, rapid_json_allocator); + + rapidjson::Value stacktrace_member, stacktrace_value; + static const char stacktrace_str[] = "stacktrace"; + + stacktrace_member.SetString(stacktrace_str, static_cast(sizeof(stacktrace_str) - 1)); + stacktrace_value.SetString(exception_stacktrace(ex), static_cast(strlen(exception_stacktrace(ex)))); + json_map.AddMember(stacktrace_member, stacktrace_value, rapid_json_allocator); + } + else if (id == TYPE_THROWABLE) + { + rapidjson::Value &json_map = json_v->SetObject(); + + throwable th = value_to_throwable(v); + + static const char str[] = "ExceptionThrown"; + + size_t size = sizeof(str); + + rapidjson::SizeType length = static_cast(size - 1); + + rapidjson::Value json_member, json_inner_value; + + json_member.SetString(str, length); + + rapid_json_serial_impl_serialize_value(throwable_value(th), &json_inner_value); + + json_map.AddMember(json_member, json_inner_value, rapid_json_allocator); + } else if (id == TYPE_PTR) { std::ostringstream ostream; @@ -306,7 +328,7 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json std::string s = ostream.str(); - json_v->SetString(s.c_str(), (rapidjson::SizeType)s.length()); + json_v->SetString(s.c_str(), static_cast(s.length())); } else if (id == TYPE_NULL) { @@ -314,33 +336,18 @@ void rapid_json_serial_impl_serialize_value(value v, RapidJSONSerialValue * json } } -char * rapid_json_serial_impl_document_stringify(RapidJSONSerialDocument * document, size_t * size) +char *rapid_json_serial_impl_document_stringify(rapid_json_document document, size_t *size) { - char * buffer_str; - - size_t buffer_size, buffer_str_size; - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - - document->Accept(writer); - - /* StringBuffer does not contain '\0' character so buffer size equals to buffer_str length */ - buffer_size = buffer.GetSize(); - - buffer_str_size = buffer_size + 1; - - RapidJSONSerialDocument::AllocatorType & allocator = document->GetAllocator(); - - memory_allocator impl = allocator.Impl(); - - buffer_str = static_cast(memory_allocator_allocate(impl, sizeof(char) * buffer_str_size)); + document->impl.Accept(writer); + size_t buffer_size = buffer.GetSize(); + size_t buffer_str_size = buffer_size + 1; + char *buffer_str = static_cast(memory_allocator_allocate(document->allocator, sizeof(char) * buffer_str_size)); if (buffer_str == NULL) { log_write("metacall", LOG_LEVEL_ERROR, "Invalid string allocation for document stringifycation in RapidJSON implementation"); - return NULL; } @@ -353,9 +360,9 @@ char * rapid_json_serial_impl_document_stringify(RapidJSONSerialDocument * docum return buffer_str; } -char * rapid_json_serial_impl_serialize(serial_impl_handle handle, value v, size_t * size) +char *rapid_json_serial_impl_serialize(serial_handle handle, value v, size_t *size) { - RapidJSONSerialDocument * document = (RapidJSONSerialDocument *)handle; + rapid_json_document document = static_cast(handle); if (handle == NULL || v == NULL || size == NULL) { @@ -364,14 +371,12 @@ char * rapid_json_serial_impl_serialize(serial_impl_handle handle, value v, size return NULL; } - RapidJSONSerialDocument::AllocatorType & allocator = document->GetAllocator(); - - rapid_json_serial_impl_serialize_value(v, document, allocator); + rapid_json_serial_impl_serialize_value(v, &document->impl); return rapid_json_serial_impl_document_stringify(document, size); } -value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) +value rapid_json_serial_impl_deserialize_value(const rapidjson::Value *v) { if (v->IsNull()) { @@ -397,6 +402,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) { unsigned int ui = v->GetUint(); + /* TODO: Review this, in case of underflow/overflow store it in a bigger type? */ log_write("metacall", LOG_LEVEL_WARNING, "Casting unsigned integer to integer (posible overflow) in RapidJSON implementation"); return value_create_int((int)ui); @@ -405,13 +411,21 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) { int64_t i = v->GetInt64(); + /* TODO: Review this, in case of underflow/overflow store it in a bigger type? */ +#if LONG_MAX < INT64_MAX + log_write("metacall", LOG_LEVEL_WARNING, "Casting long to int (posible overflow) in RapidJSON implementation"); +#endif + return value_create_long((long)i); } else if (v->IsUint64() == true) { uint64_t ui = v->GetUint64(); + /* TODO: Review this, in case of underflow/overflow store it in a bigger type? */ +#if LONG_MAX < UINT64_MAX log_write("metacall", LOG_LEVEL_WARNING, "Casting unsigned long to int (posible overflow) in RapidJSON implementation"); +#endif return value_create_long((long)ui); } @@ -431,7 +445,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) { rapidjson::SizeType length = v->GetStringLength(); - const char * str = v->GetString(); + const char *str = v->GetString(); return value_create_string(str, (size_t)length); } @@ -441,7 +455,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) value v_array = value_create_array(NULL, size); - value * values; + value *values; size_t index = 0; @@ -452,9 +466,23 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) values = static_cast(value_to_array(v_array)); - for (RapidJSONSerialValue::ConstValueIterator it = v->Begin(); it != v->End(); ++it) + for (rapidjson::Value::ConstValueIterator it = v->Begin(); it != v->End(); ++it) { - values[index++] = rapid_json_serial_impl_deserialize_value(it); + values[index] = rapid_json_serial_impl_deserialize_value(it); + + if (values[index] == NULL) + { + for (size_t iterator = 0; iterator < index; ++iterator) + { + value_type_destroy(values[iterator]); + } + + value_destroy(v_array); + + return NULL; + } + + ++index; } return v_array; @@ -465,7 +493,7 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) value v_map = value_create_map(NULL, size); - value * tuples; + value *tuples; size_t index = 0; @@ -476,14 +504,30 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) tuples = static_cast(value_to_map(v_map)); - for (RapidJSONSerialValue::ConstMemberIterator it = v->MemberBegin(); it != v->MemberEnd(); ++it) + for (rapidjson::Value::ConstMemberIterator it = v->MemberBegin(); it != v->MemberEnd(); ++it) { - const value tupla[] = - { + const value tupla[] = { rapid_json_serial_impl_deserialize_value(&it->name), rapid_json_serial_impl_deserialize_value(&it->value) }; + if (tupla[0] == NULL || tupla[1] == NULL) + { + if (tupla[0] != NULL) + { + value_type_destroy(tupla[0]); + } + + if (tupla[1] != NULL) + { + value_type_destroy(tupla[1]); + } + + value_type_destroy(v_map); + + return NULL; + } + tuples[index++] = value_create_array(tupla, sizeof(tupla) / sizeof(tupla[0])); } @@ -495,9 +539,9 @@ value rapid_json_serial_impl_deserialize_value(const RapidJSONSerialValue * v) return NULL; } -value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * buffer, size_t size) +value rapid_json_serial_impl_deserialize(serial_handle handle, const char *buffer, size_t size) { - RapidJSONSerialDocument * document = (RapidJSONSerialDocument *)handle; + rapid_json_document document = static_cast(handle); if (handle == NULL || buffer == NULL || size == 0) { @@ -506,36 +550,27 @@ value rapid_json_serial_impl_deserialize(serial_impl_handle handle, const char * return NULL; } - rapidjson::ParseResult parse_result = document->Parse(buffer, size - 1); + rapidjson::ParseResult parse_result = document->impl.Parse(buffer, size - 1); if (parse_result.IsError() == true) { - const RAPIDJSON_ERROR_CHARTYPE * error_message = rapidjson::GetParseError_En(parse_result.Code()); + const RAPIDJSON_ERROR_CHARTYPE *error_message = rapidjson::GetParseError_En(parse_result.Code()); log_write("metacall", LOG_LEVEL_ERROR, "Invalid parsing of document (%s) in RapidJSON implementation: %s at %" PRIuS, buffer, error_message, parse_result.Offset()); - delete document; - return NULL; } - return rapid_json_serial_impl_deserialize_value(document); + return rapid_json_serial_impl_deserialize_value(&document->impl); } -int rapid_json_serial_impl_destroy(serial_impl_handle handle) +int rapid_json_serial_impl_destroy(serial_handle handle) { - RapidJSONSerialDocument * document = (RapidJSONSerialDocument *)handle; + rapid_json_document document = static_cast(handle); - if (document != nullptr) + if (document != NULL) { - RapidJSONSerialDocument::AllocatorType * allocator = &document->GetAllocator(); - - if (allocator != nullptr) - { - delete allocator; - } - delete document; } diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index 156359fe3a..2f2e6bb890 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -12,7 +12,7 @@ if("${CMAKE_VERSION}" VERSION_LESS "3.11" AND POLICY CMP0037) set_policy(CMP0037 OLD) endif() -set(GTEST_VERSION 1.8.1) +set(GTEST_VERSION 1.16.0) find_package(GTest ${GTEST_VERSION}) @@ -20,7 +20,7 @@ if(NOT GTEST_FOUND) include(InstallGTest) if(NOT GTEST_FOUND) - message(STATUS "GTest libraries not found") + message(SEND_ERROR "GTest libraries not found") return() endif() @@ -47,15 +47,12 @@ endif() # Set memory check configuration option(OPTION_TEST_MEMORYCHECK "Run tests with memory checker (valgrind)." OFF) -if(OPTION_TEST_MEMORYCHECK) - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --leak-check=full") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --trace-children=yes") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --show-reachable=yes") - set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --num-callers=50") - set(MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/memcheck/valgrind.supp") - set(MEMORYCHECK_SUPPRESSIONS_FILE "${MEMORYCHECK_SUPPRESSIONS_FILE};${CMAKE_CURRENT_SOURCE_DIR}/memcheck/valgrind-python.supp") +if(OPTION_TEST_MEMORYCHECK AND (OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER OR OPTION_BUILD_MEMORY_SANITIZER)) + message(WARNING "OPTION_TEST_MEMORYCHECK and OPTION_BUILD_{ADDRESS,THREAD,MEMORY}_SANITIZER are not compatible, disabling memcheck tests.") +endif() +if(OPTION_TEST_MEMORYCHECK AND NOT (OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER OR OPTION_BUILD_MEMORY_SANITIZER)) # TODO: Memory check does not work properly with CoreCLR # # Remove MEMCHECK_IGNORE label from the following tests: @@ -65,14 +62,19 @@ if(OPTION_TEST_MEMORYCHECK) # - metacall-inspect-test # - metacall-integration-test + include(ProcessorCount) + + ProcessorCount(N) + add_custom_target(memcheck WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${CMAKE_CTEST_COMMAND} + -j${N} --label-exclude MEMCHECK_IGNORE --force-new-ctest-process --test-action memcheck - --timeout 60 - #COMMAND cat "${CMAKE_BINARY_DIR}/Testing/Temporary/MemoryChecker.*.log" + --timeout 5400 + COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_BINARY_DIR}/Testing/Temporary/MemoryChecker.*.log" ) endif() @@ -85,35 +87,69 @@ include(CTest) add_subdirectory(preprocessor_test) add_subdirectory(environment_test) add_subdirectory(log_test) +add_subdirectory(log_custom_test) add_subdirectory(adt_set_test) add_subdirectory(adt_trie_test) +add_subdirectory(adt_vector_test) +add_subdirectory(adt_map_test) add_subdirectory(reflect_value_cast_test) add_subdirectory(reflect_function_test) +add_subdirectory(reflect_object_class_test) add_subdirectory(reflect_scope_test) add_subdirectory(reflect_metadata_test) add_subdirectory(dynlink_test) add_subdirectory(detour_test) add_subdirectory(serial_test) add_subdirectory(configuration_test) -add_subdirectory(py_loader_test) -#add_subdirectory(py_django_integration_test) # TODO: Solve deadlock -add_subdirectory(py_loader_port_test) -add_subdirectory(rb_loader_test) add_subdirectory(rb_loader_parser_test) -add_subdirectory(rb_loader_parser_integration_test) -#add_subdirectory(rb_rails_integration_test) # TODO -add_subdirectory(cs_loader_test) -add_subdirectory(node_loader_test) -add_subdirectory(file_loader_test) -add_subdirectory(loader_path_test) +add_subdirectory(portability_path_test) +add_subdirectory(metacall_logs_test) add_subdirectory(metacall_load_memory_test) +add_subdirectory(metacall_load_memory_empty_test) add_subdirectory(metacall_load_configuration_test) +add_subdirectory(metacall_load_configuration_fail_test) add_subdirectory(metacall_load_configuration_relative_test) +add_subdirectory(metacall_load_configuration_python_node_test) +add_subdirectory(metacall_load_configuration_node_python_test) add_subdirectory(metacall_duplicated_handle_test) +add_subdirectory(metacall_duplicated_symbols_test) +add_subdirectory(metacall_handle_export_test) +add_subdirectory(metacall_handle_get_test) add_subdirectory(metacall_test) add_subdirectory(metacall_node_test) +add_subdirectory(metacall_node_event_loop_test) +add_subdirectory(metacall_node_event_loop_signal_test) +#add_subdirectory(metacall_node_default_export_test) # TODO: This is a feature in order to export by default all functions if there is no module.exports (bootstrap.js) add_subdirectory(metacall_node_call_test) add_subdirectory(metacall_node_inline_test) +add_subdirectory(metacall_node_async_test) +add_subdirectory(metacall_node_async_multiple_test) +add_subdirectory(metacall_node_reentrant_test) +add_subdirectory(metacall_node_port_test) +add_subdirectory(metacall_node_port_await_test) +add_subdirectory(metacall_node_port_rs_test) +add_subdirectory(metacall_node_port_c_lib_test) +add_subdirectory(metacall_node_python_port_mock_test) +add_subdirectory(metacall_node_python_port_ruby_test) +add_subdirectory(metacall_node_python_ruby_test) +add_subdirectory(metacall_node_callback_test) +add_subdirectory(metacall_node_fail_test) +add_subdirectory(metacall_node_fail_env_var_test) +add_subdirectory(metacall_node_fail_load_leak_test) +add_subdirectory(metacall_node_typescript_test) +add_subdirectory(metacall_node_python_async_after_destroy_test) +add_subdirectory(metacall_node_python_await_test) +# add_subdirectory(metacall_node_python_await_extended_test) # TODO: https://github.com/metacall/core/issues/519 +add_subdirectory(metacall_node_python_exception_test) +add_subdirectory(metacall_node_clear_mem_test) +add_subdirectory(metacall_node_async_resources_test) +add_subdirectory(metacall_node_await_chain_test) +add_subdirectory(metacall_node_exception_test) +add_subdirectory(metacall_node_python_deadlock_test) +# add_subdirectory(metacall_node_signal_handler_test) # Note: Not used anymore but leaving it here for reference to solve this: https://github.com/metacall/core/issues/121 +add_subdirectory(metacall_node_native_code_test) +add_subdirectory(metacall_node_extension_test) +add_subdirectory(metacall_node_multithread_deadlock_test) add_subdirectory(metacall_distributable_test) add_subdirectory(metacall_cast_test) add_subdirectory(metacall_init_fini_test) @@ -122,15 +158,92 @@ add_subdirectory(metacall_inspect_test) add_subdirectory(metacall_integration_test) add_subdirectory(metacall_depends_test) add_subdirectory(metacall_configuration_exec_path_test) +add_subdirectory(metacall_configuration_exec_relative_path_test) +add_subdirectory(metacall_configuration_default_test) add_subdirectory(metacall_clear_test) -add_subdirectory(metacall_python_class_test) +add_subdirectory(metacall_python_test) +add_subdirectory(metacall_python_object_class_test) add_subdirectory(metacall_python_gc_test) add_subdirectory(metacall_python_open_test) +add_subdirectory(metacall_python_dict_test) add_subdirectory(metacall_python_model_test) add_subdirectory(metacall_python_pointer_test) +add_subdirectory(metacall_python_reentrant_test) +add_subdirectory(metacall_python_varargs_test) +add_subdirectory(metacall_python_loader_port_test) +add_subdirectory(metacall_python_port_test) +add_subdirectory(metacall_python_port_https_test) +add_subdirectory(metacall_python_port_callback_test) +add_subdirectory(metacall_python_port_pointer_test) +add_subdirectory(metacall_python_port_import_test) +add_subdirectory(metacall_python_callback_test) +add_subdirectory(metacall_python_fail_test) +add_subdirectory(metacall_python_relative_path_test) +add_subdirectory(metacall_python_without_functions_test) +add_subdirectory(metacall_python_builtins_test) +add_subdirectory(metacall_python_async_test) +# TODO: add_subdirectory(metacall_python_await_test) # TODO: Implement metacall_await in Python Port +add_subdirectory(metacall_python_exception_test) +# TODO: add_subdirectory(metacall_python_node_await_test) # TODO: Implement metacall_await in Python Port +add_subdirectory(metacall_python_without_env_vars_test) add_subdirectory(metacall_map_test) +add_subdirectory(metacall_map_await_test) add_subdirectory(metacall_initialize_test) add_subdirectory(metacall_initialize_ex_test) add_subdirectory(metacall_reinitialize_test) +add_subdirectory(metacall_initialize_destroy_multiple_test) +add_subdirectory(metacall_initialize_destroy_multiple_node_test) +add_subdirectory(metacall_reload_functions_test) +add_subdirectory(metacall_invalid_loader_test) add_subdirectory(metacall_fork_test) add_subdirectory(metacall_return_monad_test) +add_subdirectory(metacall_callback_complex_test) +add_subdirectory(metacall_ruby_fail_test) +add_subdirectory(metacall_ruby_fail_empty_test) +add_subdirectory(metacall_ruby_object_class_test) +add_subdirectory(metacall_ruby_parser_integration_test) +# add_subdirectory(metacall_ruby_rails_integration_test) # TODO +add_subdirectory(metacall_function_test) +add_subdirectory(metacall_cobol_test) +add_subdirectory(metacall_file_test) +add_subdirectory(metacall_file_fail_test) +add_subdirectory(metacall_file_glob_test) +add_subdirectory(metacall_typescript_test) +add_subdirectory(metacall_typescript_node_test) +add_subdirectory(metacall_typescript_call_map_test) +add_subdirectory(metacall_typescript_tsx_test) +add_subdirectory(metacall_typescript_tsx_loop_fail_test) +add_subdirectory(metacall_typescript_require_test) +add_subdirectory(metacall_typescript_jsx_default_test) +add_subdirectory(metacall_lua_test) +add_subdirectory(metacall_rpc_test) +#add_subdirectory(metacall_csharp_function_test) # TODO: C# 9.0 seems not to work so top level expressions do not work +add_subdirectory(metacall_csharp_static_class_test) +add_subdirectory(metacall_llvm_test) +add_subdirectory(metacall_ruby_test) +add_subdirectory(metacall_cs_test) +add_subdirectory(metacall_julia_test) +add_subdirectory(metacall_java_test) +add_subdirectory(metacall_wasm_test) +add_subdirectory(metacall_wasm_python_port_test) +add_subdirectory(metacall_rust_test) +add_subdirectory(metacall_rust_load_from_mem_test) +add_subdirectory(metacall_rust_load_from_package_test) +add_subdirectory(metacall_rust_load_from_package_dep_test) +add_subdirectory(metacall_rust_load_from_package_class_test) +add_subdirectory(metacall_rust_class_test) +add_subdirectory(metacall_c_test) +add_subdirectory(metacall_c_lib_test) +add_subdirectory(metacall_version_test) +add_subdirectory(metacall_dynlink_path_test) +add_subdirectory(metacall_library_path_without_env_vars_test) +add_subdirectory(metacall_ext_test) +add_subdirectory(metacall_plugin_extension_test) +add_subdirectory(metacall_plugin_extension_local_test) +add_subdirectory(metacall_plugin_extension_destroy_order_test) +add_subdirectory(metacall_plugin_extension_invalid_path_test) +add_subdirectory(metacall_cli_core_plugin_test) +add_subdirectory(metacall_cli_core_plugin_await_test) +add_subdirectory(metacall_backtrace_plugin_test) +add_subdirectory(metacall_sandbox_plugin_test) +add_subdirectory(metacall_cxx_port_test) diff --git a/source/tests/adt_map_test/CMakeLists.txt b/source/tests/adt_map_test/CMakeLists.txt new file mode 100644 index 0000000000..0767bcbb2b --- /dev/null +++ b/source/tests/adt_map_test/CMakeLists.txt @@ -0,0 +1,142 @@ +# +# Executable name and options +# + +# Target name +set(target adt-map-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/adt_map_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::adt + +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/adt_map_test/source/adt_map_test.cpp b/source/tests/adt_map_test/source/adt_map_test.cpp new file mode 100644 index 0000000000..24217e3834 --- /dev/null +++ b/source/tests/adt_map_test/source/adt_map_test.cpp @@ -0,0 +1,247 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +typedef char key_str[7]; + +static size_t iterator_counter = 0; +static std::vector order_iterate; +static std::vector order_iterator; + +int map_cb_iterate_str_to_int(map m, map_key key, map_value value, map_cb_iterate_args args) +{ + if (m && args == NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "%s -> %d", (char *)key, *((int *)(value))); + + order_iterate.push_back(*((int *)(value))); + + ++iterator_counter; + + return 0; + } + + return 1; +} + +class adt_map_test : public testing::Test +{ +public: +}; + +TEST_F(adt_map_test, map_int) +{ + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + map m = map_create(&hash_callback_str, &comparable_callback_str); + + static key_str key_array[] = { + "aaaaaa", "aaaaab", "aaaaba", "aaaabb", + "aaabaa", "aaabab", "aaabba", "aaabbb", + "aabaaa", "aabaab", "aababa", "aababb", + "aabbaa", "aabbab", "aabbba", "aabbbb", + "abaaaa", "abaaab", "abaaba", "abaabb", + "ababaa", "ababab", "ababbb", "abbaaa", + "abbaab", "abbaba", "abbabb", "abbbaa", + "abbbab", "abbbba", "abbbbb", "baaaaa", + "baaaab", "baaaba", "baaabb", "baabaa", + "baabab", "baabba", "baabbb", "babaaa", + "babaab", "bababa", "bababb", "babbaa", + "babbab", "babbba", "babbbb", "bbaaaa", + "bbaaab", "bbaaba", "bbaabb", "bbabaa", + "bbabab", "bbabba", "bbabbb", "bbbaaa", + "bbbaab", "bbbaba", "bbbabb", "bbbbaa", + "bbbbab", "bbbbba", "bbbbbb" + }; + + static const size_t key_array_size = sizeof(key_array) / sizeof(key_array[0]); + + static int value_array[] = { + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72 + }; + + static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + + EXPECT_EQ((size_t)key_array_size, (size_t)value_array_size); + + /* Insert value */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)map_insert(m, key_array[i], &value_array[i])); + } + + /* Insert duplicated values */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)map_insert(m, key_array[i], &value_array[value_array_size - 1 - i])); + } + + map_iterate(m, &map_cb_iterate_str_to_int, NULL); + + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size * 2); + + /* Iterators */ + iterator_counter = 0; + struct map_iterator_type it; + + for (map_iterator_begin(&it, m); map_iterator_end(&it) != 0; map_iterator_next(&it)) + { + char *key = (char *)map_iterator_key(&it); + int *value = (int *)map_iterator_value(&it); + + log_write("metacall", LOG_LEVEL_DEBUG, "[%s -> %d]", (char *)key, *((int *)(value))); + + order_iterator.push_back(*((int *)(value))); + + iterator_counter++; + } + + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size * 2); + + EXPECT_EQ((bool)true, (bool)(order_iterator == order_iterate)); + + /* Get value */ + for (size_t i = 0; i < key_array_size; ++i) + { + vector v = map_get(m, key_array[i]); + + EXPECT_EQ((size_t)vector_size(v), (size_t)2); + + int *value0 = vector_at_type(v, 0, int *); + + EXPECT_EQ((int)(value_array[i] == *value0 || value_array[value_array_size - 1 - i] == *value0), (int)1); + + int *value1 = vector_at_type(v, 1, int *); + + EXPECT_EQ((int)(value_array[i] == *value1 || value_array[value_array_size - 1 - i] == *value1), (int)1); + + log_write("metacall", LOG_LEVEL_DEBUG, "%s -> %d | %d", key_array[i], *value0, *value1); + + vector_destroy(v); + } + + EXPECT_EQ((size_t)key_array_size * 2, (size_t)map_size(m)); + + /* Remove value */ + for (size_t i = 0; i < key_array_size; ++i) + { + int *remove_value = (int *)map_remove(m, key_array[i]); + + EXPECT_EQ((int)(value_array[i] == *remove_value || value_array[value_array_size - 1 - i] == *remove_value), (int)1); + } + + EXPECT_EQ((size_t)key_array_size, (size_t)map_size(m)); + + /* Remove multiple values */ + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[5])); + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[6])); + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[7])); + EXPECT_EQ((int)0, (int)map_insert(m, key_array[0], &value_array[8])); + + vector v = map_remove_all(m, key_array[0]); + + EXPECT_NE((vector)NULL, (vector)v); + + EXPECT_EQ((size_t)5, (size_t)vector_size(v)); + + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 0, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 1, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 2, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 3, int *))); + log_write("metacall", LOG_LEVEL_DEBUG, "Deleted %d", *(vector_at_type(v, 4, int *))); + + vector_destroy(v); + + log_write("metacall", LOG_LEVEL_DEBUG, "vvvvvvvvvvvvvvvv - This invalid message is correct, we are testing for a invalid remove"); + v = map_remove_all(m, key_array[0]); + log_write("metacall", LOG_LEVEL_DEBUG, "^^^^^^^^^^^^^^^^ - This invalid message is correct, we are testing for a invalid remove"); + + EXPECT_EQ((vector)NULL, (vector)v); + + EXPECT_EQ((size_t)key_array_size - 1, (size_t)map_size(m)); + + map_destroy(m); +} + +TEST_F(adt_map_test, map_structs) +{ + typedef struct stru_type + { + const char *str; + int a; + } * stru; + + static const char k[] = "random"; + + stru a = new stru_type(); + a->str = k; + a->a = 123; + + stru b = new stru_type(); + b->str = k; + b->a = 321; + + map m = map_create(&hash_callback_str, &comparable_callback_str); + + EXPECT_EQ((int)0, (int)map_insert(m, (map_key)a->str, a)); + EXPECT_EQ((int)0, (int)map_insert(m, (map_key)b->str, b)); + + vector v = map_get(m, (map_key)k); + + EXPECT_EQ((size_t)vector_size(v), (size_t)2); + + stru value0 = vector_at_type(v, 0, stru); + + EXPECT_EQ((int)(123 == value0->a || 321 == value0->a), (int)1); + + stru value1 = vector_at_type(v, 1, stru); + + EXPECT_EQ((int)(123 == value1->a || 321 == value1->a), (int)1); + + EXPECT_EQ((int)(a == value0 || a == value1), (int)1); + EXPECT_EQ((int)(b == value0 || b == value1), (int)1); + + log_write("metacall", LOG_LEVEL_DEBUG, "A-B %p | %p", a, b); + log_write("metacall", LOG_LEVEL_DEBUG, "val %p | %p", value0, value1); + + vector_destroy(v); + + map_destroy(m); + + delete a; + delete b; +} diff --git a/source/tests/adt_map_test/source/main.cpp b/source/tests/adt_map_test/source/main.cpp new file mode 100644 index 0000000000..24fae6af69 --- /dev/null +++ b/source/tests/adt_map_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/adt_set_test/CMakeLists.txt b/source/tests/adt_set_test/CMakeLists.txt index 8e6c231ec2..a3d3b1c3f0 100644 --- a/source/tests/adt_set_test/CMakeLists.txt +++ b/source/tests/adt_set_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::adt @@ -106,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/adt_set_test/source/adt_set_test.cpp b/source/tests/adt_set_test/source/adt_set_test.cpp index 9b9028b26b..85c087b9ce 100644 --- a/source/tests/adt_set_test/source/adt_set_test.cpp +++ b/source/tests/adt_set_test/source/adt_set_test.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -27,6 +27,8 @@ typedef char key_str[7]; static size_t iterator_counter = 0; +static std::vector order_iterate; +static std::vector order_iterator; int set_cb_iterate_str_to_int(set s, set_key key, set_value value, set_cb_iterate_args args) { @@ -34,6 +36,8 @@ int set_cb_iterate_str_to_int(set s, set_key key, set_value value, set_cb_iterat { log_write("metacall", LOG_LEVEL_DEBUG, "%s -> %d", (char *)key, *((int *)(value))); + order_iterate.push_back(*((int *)(value))); + ++iterator_counter; return 0; @@ -42,101 +46,205 @@ int set_cb_iterate_str_to_int(set s, set_key key, set_value value, set_cb_iterat return 1; } +static size_t iterator_counter_ptr = 0; + +int set_cb_iterate_ptr_to_int(set s, set_key key, set_value value, set_cb_iterate_args args) +{ + if (s && args == NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "%p -> %d", key, *((int *)(value))); + + ++iterator_counter_ptr; + + return 0; + } + + return 1; +} + class adt_set_test : public testing::Test { - public: +public: }; TEST_F(adt_set_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); - set s = set_create(&hash_callback_str, &comparable_callback_str); - - static key_str key_array[] = - { - "aaaaaa", "aaaaab", "aaaaba", "aaaabb", - "aaabaa", "aaabab", "aaabba", "aaabbb", - "aabaaa", "aabaab", "aababa", "aababb", - "aabbaa", "aabbab", "aabbba", "aabbbb", - "abaaaa", "abaaab", "abaaba", "abaabb", - "ababaa", "ababab", "ababbb", "abbaaa", - "abbaab", "abbaba", "abbabb", "abbbaa", - "abbbab", "abbbba", "abbbbb", "baaaaa", - "baaaab", "baaaba", "baaabb", "baabaa", - "baabab", "baabba", "baabbb", "babaaa", - "babaab", "bababa", "bababb", "babbaa", - "babbab", "babbba", "babbbb", "bbaaaa", - "bbaaab", "bbaaba", "bbaabb", "bbabaa", - "bbabab", "bbabba", "bbabbb", "bbbaaa", - "bbbaab", "bbbaba", "bbbabb", "bbbbaa", - "bbbbab", "bbbbba", "bbbbbb" - }; - - static const size_t key_array_size = sizeof(key_array) / sizeof(key_array[0]); - - static int value_array[] = + /* Hash String */ { - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72 - }; + set s = set_create(&hash_callback_str, &comparable_callback_str); + + static key_str key_array[] = { + "aaaaaa", "aaaaab", "aaaaba", "aaaabb", + "aaabaa", "aaabab", "aaabba", "aaabbb", + "aabaaa", "aabaab", "aababa", "aababb", + "aabbaa", "aabbab", "aabbba", "aabbbb", + "abaaaa", "abaaab", "abaaba", "abaabb", + "ababaa", "ababab", "ababbb", "abbaaa", + "abbaab", "abbaba", "abbabb", "abbbaa", + "abbbab", "abbbba", "abbbbb", "baaaaa", + "baaaab", "baaaba", "baaabb", "baabaa", + "baabab", "baabba", "baabbb", "babaaa", + "babaab", "bababa", "bababb", "babbaa", + "babbab", "babbba", "babbbb", "bbaaaa", + "bbaaab", "bbaaba", "bbaabb", "bbabaa", + "bbabab", "bbabba", "bbabbb", "bbbaaa", + "bbbaab", "bbbaba", "bbbabb", "bbbbaa", + "bbbbab", "bbbbba", "bbbbbb" + }; + + static const size_t key_array_size = sizeof(key_array) / sizeof(key_array[0]); + + static int value_array[] = { + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72 + }; + + static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + + EXPECT_EQ((size_t)key_array_size, (size_t)value_array_size); + + /* Insert value */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)set_insert(s, key_array[i], &value_array[i])); + } - static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + set_iterate(s, &set_cb_iterate_str_to_int, NULL); - EXPECT_EQ((size_t) key_array_size, (size_t) value_array_size); + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size); - /* Insert value */ - for (size_t i = 0; i < key_array_size; ++i) - { - EXPECT_EQ((int) 0, (int) set_insert(s, key_array[i], &value_array[i])); - } + /* Iterators */ + iterator_counter = 0; + struct set_iterator_type it; - set_iterate(s, &set_cb_iterate_str_to_int, NULL); + for (set_iterator_begin(&it, s); set_iterator_end(&it) != 0; set_iterator_next(&it)) + { + char *key = (char *)set_iterator_key(&it); + int *value = (int *)set_iterator_value(&it); - EXPECT_EQ((size_t) iterator_counter, (size_t) value_array_size); + log_write("metacall", LOG_LEVEL_DEBUG, "[%s -> %d]", (char *)key, *((int *)(value))); - /* Get value */ - for (size_t i = 0; i < key_array_size; ++i) - { - int * value = (int *)set_get(s, key_array[i]); + order_iterator.push_back(*((int *)(value))); - EXPECT_NE((int *) NULL, (int *) value); + iterator_counter++; + } - EXPECT_EQ((int) value_array[i], (int) *value); - } + EXPECT_EQ((size_t)iterator_counter, (size_t)value_array_size); + + EXPECT_EQ((bool)true, (bool)(order_iterator == order_iterate)); + + /* Get value */ + for (size_t i = 0; i < key_array_size; ++i) + { + int *value = (int *)set_get(s, key_array[i]); - /* Overwrite value */ - EXPECT_EQ((int) 0, (int) set_insert(s, key_array[1], &value_array[0])); + EXPECT_NE((int *)NULL, (int *)value); - int * value = (int *)set_get(s, key_array[1]); + EXPECT_EQ((int)value_array[i], (int)*value); + } + + /* Overwrite value */ + EXPECT_EQ((int)0, (int)set_insert(s, key_array[1], &value_array[0])); - EXPECT_EQ((int) value_array[0], (int) *value); + int *value = (int *)set_get(s, key_array[1]); - EXPECT_EQ((size_t) key_array_size, (size_t) set_size(s)); + EXPECT_EQ((int)value_array[0], (int)*value); - /* Remove value */ - for (size_t i = 0; i < key_array_size; ++i) + EXPECT_EQ((size_t)key_array_size, (size_t)set_size(s)); + + /* Remove value */ + for (size_t i = 0; i < key_array_size; ++i) + { + int *remove_value = (int *)set_remove(s, key_array[i]); + + if (i == 1) + { + EXPECT_EQ((int)value_array[0], (int)*remove_value); + } + else + { + EXPECT_EQ((int)value_array[i], (int)*remove_value); + } + } + + set_destroy(s); + } + + /* Hash Pointer */ { - int * remove_value = (int *)set_remove(s, key_array[i]); + set s = set_create(&hash_callback_ptr, &comparable_callback_ptr); + + static int value_array[] = { + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72 + }; + + static const size_t value_array_size = sizeof(value_array) / sizeof(value_array[0]); + + static int key_array[value_array_size] = { 0 }; + + static const size_t key_array_size = value_array_size; + + /* Insert value */ + for (size_t i = 0; i < key_array_size; ++i) + { + EXPECT_EQ((int)0, (int)set_insert(s, &key_array[i], &value_array[i])); + } + + set_iterate(s, &set_cb_iterate_ptr_to_int, NULL); + + EXPECT_EQ((size_t)iterator_counter_ptr, (size_t)value_array_size); - if (i == 1) + /* Get value */ + for (size_t i = 0; i < key_array_size; ++i) { - EXPECT_EQ((int) value_array[0], (int) *remove_value); + int *value = (int *)set_get(s, &key_array[i]); + + EXPECT_NE((int *)NULL, (int *)value); + + EXPECT_EQ((int)value_array[i], (int)*value); } - else + + /* Overwrite value */ + EXPECT_EQ((int)0, (int)set_insert(s, &key_array[1], &value_array[0])); + + int *value = (int *)set_get(s, &key_array[1]); + + EXPECT_EQ((int)value_array[0], (int)*value); + + EXPECT_EQ((size_t)key_array_size, (size_t)set_size(s)); + + /* Remove value */ + for (size_t i = 0; i < key_array_size; ++i) { - EXPECT_EQ((int) value_array[i], (int) *remove_value); + int *remove_value = (int *)set_remove(s, &key_array[i]); + + if (i == 1) + { + EXPECT_EQ((int)value_array[0], (int)*remove_value); + } + else + { + EXPECT_EQ((int)value_array[i], (int)*remove_value); + } } - } - set_destroy(s); + set_destroy(s); + } } diff --git a/source/tests/adt_set_test/source/main.cpp b/source/tests/adt_set_test/source/main.cpp index 09764b62ba..24fae6af69 100644 --- a/source/tests/adt_set_test/source/main.cpp +++ b/source/tests/adt_set_test/source/main.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/adt_trie_test/CMakeLists.txt b/source/tests/adt_trie_test/CMakeLists.txt index dbe6da5cdb..19babf556b 100644 --- a/source/tests/adt_trie_test/CMakeLists.txt +++ b/source/tests/adt_trie_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::adt ) @@ -105,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/adt_trie_test/source/adt_trie_test.cpp b/source/tests/adt_trie_test/source/adt_trie_test.cpp index 5d5d76ab69..1d9541dc03 100644 --- a/source/tests/adt_trie_test/source/adt_trie_test.cpp +++ b/source/tests/adt_trie_test/source/adt_trie_test.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ * */ -#include +#include -#include #include +#include #include @@ -29,7 +29,7 @@ class adt_trie_test : public testing::Test { - public: +public: }; int trie_iterator_cb_print(trie t, trie_key key, trie_value value, trie_cb_iterate_args args) @@ -38,8 +38,8 @@ int trie_iterator_cb_print(trie t, trie_key key, trie_value value, trie_cb_itera if (t != NULL && key != NULL && value != NULL) { - const char * key_str = reinterpret_cast(key); - const char * value_str = reinterpret_cast(value); + const char *key_str = reinterpret_cast(key); + const char *value_str = reinterpret_cast(value); log_write("metacall", LOG_LEVEL_DEBUG, "%s : %s", key_str, value_str); @@ -67,21 +67,19 @@ int trie_iterator_cb_clear(trie t, trie_key key, trie_value value, trie_cb_itera TEST_F(adt_trie_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); size_t iterator; - static const char * keys_str[] = - { + static const char *keys_str[] = { "this", "is", "a", "path" }; - static const char * values_str[] = - { + static const char *values_str[] = { "these", "are", "the", "values" }; @@ -95,7 +93,7 @@ TEST_F(adt_trie_test, DefaultConstructor) trie suffix_trie = NULL; - trie_key * last_prefix; + trie_key *last_prefix; size_t keys_size = sizeof(keys_str) / sizeof(keys_str[0]); @@ -112,7 +110,7 @@ TEST_F(adt_trie_test, DefaultConstructor) vector_push_back(keys, &key); - EXPECT_EQ((int) 0, (int) trie_insert(t, keys, value)); + EXPECT_EQ((int)0, (int)trie_insert(t, keys, value)); } keys_copy = vector_copy(keys); @@ -121,11 +119,11 @@ TEST_F(adt_trie_test, DefaultConstructor) { trie_value value = trie_get(t, keys_copy); - const char * value_str = reinterpret_cast(value); + const char *value_str = reinterpret_cast(value); log_write("metacall", LOG_LEVEL_DEBUG, "%" PRIuS " -> %s", iterator, value_str); - EXPECT_EQ((int) 0, (int) strcmp(values_str[keys_size - iterator - 1], value_str)); + EXPECT_STREQ(values_str[keys_size - iterator - 1], value_str); vector_pop_back(keys_copy); } @@ -134,19 +132,19 @@ TEST_F(adt_trie_test, DefaultConstructor) last_prefix = reinterpret_cast(vector_back(keys)); - EXPECT_EQ((int) 0, (int) trie_prefixes(t, *last_prefix, prefixes)); + EXPECT_EQ((int)0, (int)trie_prefixes(t, *last_prefix, prefixes)); log_write("metacall", LOG_LEVEL_DEBUG, "cannonical path: "); for (iterator = 0; iterator < vector_size(prefixes); ++iterator) { - trie_key * key = reinterpret_cast(vector_at(prefixes, iterator)); + trie_key *key = reinterpret_cast(vector_at(prefixes, iterator)); - const char * key_str = reinterpret_cast(*key); + const char *key_str = reinterpret_cast(*key); log_write("metacall", LOG_LEVEL_DEBUG, "%s/", key_str); - EXPECT_EQ((int) 0, (int) strcmp(keys_str[iterator], key_str)); + EXPECT_STREQ(keys_str[iterator], key_str); } vector_pop_back(keys); diff --git a/source/tests/adt_trie_test/source/main.cpp b/source/tests/adt_trie_test/source/main.cpp index 09764b62ba..24fae6af69 100644 --- a/source/tests/adt_trie_test/source/main.cpp +++ b/source/tests/adt_trie_test/source/main.cpp @@ -2,7 +2,7 @@ * Abstract Data Type Library by Parra Studios * A abstract data type library providing generic containers. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/adt_vector_test/CMakeLists.txt b/source/tests/adt_vector_test/CMakeLists.txt new file mode 100644 index 0000000000..e454f0266d --- /dev/null +++ b/source/tests/adt_vector_test/CMakeLists.txt @@ -0,0 +1,142 @@ +# +# Executable name and options +# + +# Target name +set(target adt-vector-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/adt_vector_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::adt + +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/adt_vector_test/source/adt_vector_test.cpp b/source/tests/adt_vector_test/source/adt_vector_test.cpp new file mode 100644 index 0000000000..816c3bcf59 --- /dev/null +++ b/source/tests/adt_vector_test/source/adt_vector_test.cpp @@ -0,0 +1,54 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class adt_vector_test : public testing::Test +{ +public: +}; + +TEST_F(adt_vector_test, DefaultConstructor) +{ + static const size_t capacity = 50; + + vector v = vector_create_reserve_type(size_t, capacity); + + ASSERT_EQ((size_t)vector_type_size(v), (size_t)sizeof(size_t)); + ASSERT_EQ((size_t)vector_size(v), (size_t)0); + + for (size_t i = 0; i < capacity; ++i) + { + vector_push_back_var(v, i); + } + + ASSERT_EQ((size_t)vector_size(v), (size_t)capacity); + + for (size_t i = 0; i < capacity; ++i) + { + size_t current = vector_at_type(v, i, size_t); + + ASSERT_EQ((size_t)current, (size_t)i); + } + + vector_destroy(v); +} diff --git a/source/tests/adt_vector_test/source/main.cpp b/source/tests/adt_vector_test/source/main.cpp new file mode 100644 index 0000000000..24fae6af69 --- /dev/null +++ b/source/tests/adt_vector_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/configuration_test/CMakeLists.txt b/source/tests/configuration_test/CMakeLists.txt index 42d873e302..0700e2bf71 100644 --- a/source/tests/configuration_test/CMakeLists.txt +++ b/source/tests/configuration_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,17 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration + ${META_PROJECT_NAME}::metacall ) # @@ -111,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/configuration_test/source/configuration_test.cpp b/source/tests/configuration_test/source/configuration_test.cpp index 931f53232d..056d99493f 100644 --- a/source/tests/configuration_test/source/configuration_test.cpp +++ b/source/tests/configuration_test/source/configuration_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -32,26 +32,26 @@ class configuration_test : public testing::Test { - public: +public: }; TEST_F(configuration_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); const char key_value[] = "value"; - char * configuration_path = environment_variable_create(CONFIGURATION_PATH, NULL); + char *configuration_path = environment_variable_create(CONFIGURATION_PATH, NULL); memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); - ASSERT_NE((memory_allocator) NULL, (memory_allocator) allocator); + ASSERT_NE((memory_allocator)NULL, (memory_allocator)allocator); - ASSERT_EQ((int) 0, (int) configuration_initialize("rapid_json", configuration_path, allocator)); + ASSERT_EQ((int)0, (int)configuration_initialize("rapid_json", configuration_path, allocator)); environment_variable_destroy(configuration_path); @@ -62,19 +62,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration global = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) global); + ASSERT_NE((configuration)NULL, (configuration)global); value v = configuration_value(global, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 12345, (int) value_to_int(v)); + EXPECT_EQ((int)12345, (int)value_to_int(v)); - v = configuration_value(global, key_value_local); + v = configuration_value_type(global, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 321321, (int) value_to_int(v)); + EXPECT_EQ((int)321321, (int)value_to_int(v)); } /* Child A */ @@ -84,19 +84,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_a = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_a); + ASSERT_NE((configuration)NULL, (configuration)child_a); value v = configuration_value(child_a, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 65432345, (int) value_to_int(v)); + EXPECT_EQ((int)65432345, (int)value_to_int(v)); - v = configuration_value(child_a, key_value_local); + v = configuration_value_type(child_a, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 555, (int) value_to_int(v)); + EXPECT_EQ((int)555, (int)value_to_int(v)); } /* Child B */ @@ -106,19 +106,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_b = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_b); + ASSERT_NE((configuration)NULL, (configuration)child_b); value v = configuration_value(child_b, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 54321, (int) value_to_int(v)); + EXPECT_EQ((int)54321, (int)value_to_int(v)); - v = configuration_value(child_b, key_value_local); + v = configuration_value_type(child_b, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 333, (int) value_to_int(v)); + EXPECT_EQ((int)333, (int)value_to_int(v)); } /* Child C */ @@ -128,19 +128,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_c = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_c); + ASSERT_NE((configuration)NULL, (configuration)child_c); value v = configuration_value(child_c, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 1111, (int) value_to_int(v)); + EXPECT_EQ((int)1111, (int)value_to_int(v)); - v = configuration_value(child_c, key_value_local); + v = configuration_value_type(child_c, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 8080, (int) value_to_int(v)); + EXPECT_EQ((int)8080, (int)value_to_int(v)); } /* Child D */ @@ -150,19 +150,19 @@ TEST_F(configuration_test, DefaultConstructor) configuration child_d = configuration_scope(name); - ASSERT_NE((configuration) NULL, (configuration) child_d); + ASSERT_NE((configuration)NULL, (configuration)child_d); value v = configuration_value(child_d, key_value); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 22222, (int) value_to_int(v)); + EXPECT_EQ((int)22222, (int)value_to_int(v)); - v = configuration_value(child_d, key_value_local); + v = configuration_value_type(child_d, key_value_local, TYPE_INT); - ASSERT_NE((value) NULL, (value) v); + ASSERT_NE((value)NULL, (value)v); - EXPECT_EQ((int) 999999, (int) value_to_int(v)); + EXPECT_EQ((int)999999, (int)value_to_int(v)); } configuration_destroy(); diff --git a/source/tests/configuration_test/source/main.cpp b/source/tests/configuration_test/source/main.cpp index 96ee90e40f..4b3063b2f5 100644 --- a/source/tests/configuration_test/source/main.cpp +++ b/source/tests/configuration_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/cs_loader_test/CMakeLists.txt b/source/tests/cs_loader_test/CMakeLists.txt deleted file mode 100644 index 7732fb5523..0000000000 --- a/source/tests/cs_loader_test/CMakeLists.txt +++ /dev/null @@ -1,153 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_CS) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target cs-loader-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(headers - ${include_path}/environment.hpp -) - -set(sources - ${source_path}/main.cpp - ${source_path}/environment.cpp - ${source_path}/cs_loader_test.cpp -) - -# Group source files -set(header_group "Header Files") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} - ${headers} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::metacall -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/cs_loader_test/source/cs_loader_test.cpp b/source/tests/cs_loader_test/source/cs_loader_test.cpp deleted file mode 100644 index 2f5cb66f7f..0000000000 --- a/source/tests/cs_loader_test/source/cs_loader_test.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for dynamic loading and linking shared objects at run-time. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -#include - -#include - -class cs_loader_test : public testing::Test -{ -protected: -}; - -TEST_F(cs_loader_test, SayHello) -{ - metacall("SayHello"); -} - -TEST_F(cs_loader_test, SayAny) -{ - metacall("Say", "Any"); -} - -TEST_F(cs_loader_test, Jump) -{ - value ret = NULL; - - ret = metacall("SuperJump"); - - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((int) 2, (int) value_to_long(ret)); - - value_destroy(ret); -} - -TEST_F(cs_loader_test, Sum) -{ - value ret = NULL; - - ret = metacall("Sum", 5, 10); - - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((int) 15, (int) value_to_long(ret)); - - value_destroy(ret); -} - -TEST_F(cs_loader_test, Concat) -{ - value ret = NULL; - - ret = metacall("Concat", "Hello ", "World"); - - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((int) 0, (int) strcmp((const char *)value_to_string(ret), "Hello World")); - - value_destroy(ret); -} diff --git a/source/tests/cs_loader_test/source/environment.cpp b/source/tests/cs_loader_test/source/environment.cpp deleted file mode 100644 index 05a5d4450f..0000000000 --- a/source/tests/cs_loader_test/source/environment.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for dynamic loading and linking shared objects at run-time. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include - -#include - -#include - -void environment::SetUp() -{ - const char * cs_scripts[] = - { - "hello.cs", - "IJump.cs", - "JumpMaster.cs", - "SuperJump.cs", - "TinyJump.cs" - }; - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); -} - -void environment::TearDown() -{ - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/cs_loader_test/source/main.cpp b/source/tests/cs_loader_test/source/main.cpp deleted file mode 100644 index 008c3a9673..0000000000 --- a/source/tests/cs_loader_test/source/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - ::testing::AddGlobalTestEnvironment(new environment()); - return RUN_ALL_TESTS(); -} diff --git a/source/tests/detour_test/CMakeLists.txt b/source/tests/detour_test/CMakeLists.txt index 5cac538742..06e3ef9794 100644 --- a/source/tests/detour_test/CMakeLists.txt +++ b/source/tests/detour_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if detours are enabled -if(NOT OPTION_FORK_SAFE) +if(NOT OPTION_BUILD_DETOURS OR NOT OPTION_BUILD_DETOURS_PLTHOOK) return() endif() @@ -89,10 +89,13 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ${META_PROJECT_NAME}::memory + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::adt ${META_PROJECT_NAME}::dynlink + ${META_PROJECT_NAME}::plugin ${META_PROJECT_NAME}::detour ) @@ -114,11 +117,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +143,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + plthook_detour +) + # # Define test properties # diff --git a/source/tests/detour_test/source/detour_test.cpp b/source/tests/detour_test/source/detour_test.cpp index 058a2afe18..4882867e83 100644 --- a/source/tests/detour_test/source/detour_test.cpp +++ b/source/tests/detour_test/source/detour_test.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,75 +18,146 @@ * */ -#include +#include #include #include +#include + #include class detour_test : public testing::Test { - public: +public: }; -static detour_handle handle; +static detour_handle handle = NULL; +static const char *(*trampoline)(void) = NULL; -int hook_function(int x) +int check_detour_hook(const char *(*fp)(void)) { - EXPECT_EQ((int) 2, (int) x); + static const char str_without_hook[] = "Detour Library"; - log_write("metacall", LOG_LEVEL_DEBUG, "Hook function %d", x); + const char *str = fp(); - int (*target_function_ptr)(int) = (int(*)(int))detour_trampoline(handle); + log_write("metacall", LOG_LEVEL_DEBUG, "Check: %s", str); - return target_function_ptr(x + 4) + 2; + return strncmp(str, str_without_hook, sizeof(str_without_hook) - 1); } -int target_function(int x) +const char *hook_function(void) { - EXPECT_EQ((int) 6, (int) x); + static const char str_with_hook[] = "Yeet"; + + log_write("metacall", LOG_LEVEL_DEBUG, "HOOK WORKING PROPERLY"); + log_write("metacall", LOG_LEVEL_DEBUG, "Original function: %s", trampoline()); + + /* Here we check that we got the correct trampoline implementation (aka the original function) + and we can call it from inside of the body of the hook function */ + EXPECT_EQ((int)0, (int)check_detour_hook(trampoline)); + return str_with_hook; +} + +/* TODO: +* This test is not going to work because detour_enumeration does not walk in +* the following sections: +* T Global text symbol +* t Local text symbol +* This funtion we are searching for is stored in: +* 0000000000073630 T test_exported_symbols_from_executable +* 00000000000736e0 t _Z13hook_functionv +* 0000000000072e34 t _Z13hook_functionv.cold +* 0000000000073680 t _Z17check_detour_hookPFPKcvE +* We can find all the sections here: https://en.wikipedia.org/wiki/Nm_(Unix) +* For listing properly all the symbols we should replicate something like +* GNU libc does under the hood for dlsym, which is implemented through do_lookup: +* https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=elf/dl-lookup.c;hb=HEAD +* We will leave this for future versions, including support for GNU hashed symbols. +*/ +#define TODO_TEST_EXPORTED_SYMBOLS_FROM_EXECUTABLE 1 + +#ifdef _WIN32 + #define EXPORT_SYMBOL __declspec(dllexport) +#else + #define EXPORT_SYMBOL __attribute__((visibility("default"))) +#endif + +extern "C" EXPORT_SYMBOL int test_exported_symbols_from_executable(int x) +{ log_write("metacall", LOG_LEVEL_DEBUG, "Target function %d", x); - return 4; + return x; } TEST_F(detour_test, DefaultConstructor) { - static const char name[] = "funchook"; + static const char name[] = "plthook"; /* Initialize log */ - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); /* Initialize detour */ - EXPECT_EQ((int) 0, (int) detour_initialize()); + EXPECT_EQ((int)0, (int)detour_initialize()); - /* Create detour funchook */ + /* Create detour plthook */ detour d = detour_create(name); - EXPECT_NE((detour) NULL, (detour) d); + ASSERT_NE((detour)NULL, (detour)d); + + EXPECT_STREQ(name, detour_name(d)); + + /* Load detour of detour library */ + handle = detour_load_file(d, NULL); + + ASSERT_NE((detour_handle)NULL, (detour_handle)handle); + + /* Check if it can list exported symbols from executable */ +#ifndef TODO_TEST_EXPORTED_SYMBOLS_FROM_EXECUTABLE + test_exported_symbols_from_executable(3); + + unsigned int position = 0; + const char *fn_name = NULL; + void (**addr)(void) = NULL; + bool found = false; + while (detour_enumerate(d, handle, &position, &fn_name, &addr) == 0) + { + log_write("metacall", LOG_LEVEL_DEBUG, "[%d] %p %s", position, *addr, fn_name); + + if (strcmp("test_exported_symbols_from_executable", fn_name) == 0) + { + found = true; + EXPECT_EQ((void *)(*addr), (void *)(&test_exported_symbols_from_executable)); + break; + } + } - EXPECT_EQ((int) 0, (int) strcmp(name, detour_name(d))); + EXPECT_EQ((bool)true, (bool)found); +#endif /* Install detour */ - handle = detour_install(d, (void(*)(void))&target_function, (void(*)(void))&hook_function); + union + { + const char *(**trampoline)(void); + void (**ptr)(void); + } cast = { &trampoline }; - EXPECT_NE((detour_handle) NULL, (detour_handle) handle); + ASSERT_EQ((int)0, detour_replace(d, handle, "detour_print_info", (void (*)(void))(&hook_function), cast.ptr)); - /* Call detour, it should call hooked function */ - EXPECT_EQ((int) 6, (int) target_function(2)); + /* This must return "Yeet", so when checking the test it should return distinct from 0, then the funtion is properly hooked */ + EXPECT_NE((int)0, (int)check_detour_hook(&detour_print_info)); /* Uninstall detour */ - EXPECT_EQ((int) 0, (int) detour_uninstall(d, handle)); + detour_unload(d, handle); /* Clear detour */ - EXPECT_EQ((int) 0, (int) detour_clear(d)); + EXPECT_EQ((int)0, (int)detour_clear(d)); /* Destroy detour */ detour_destroy(); diff --git a/source/tests/detour_test/source/main.cpp b/source/tests/detour_test/source/main.cpp index 8f29968341..ccd0ee35a6 100644 --- a/source/tests/detour_test/source/main.cpp +++ b/source/tests/detour_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/dynlink_test/CMakeLists.txt b/source/tests/dynlink_test/CMakeLists.txt index 2ae5afddd8..ae0eabeeea 100644 --- a/source/tests/dynlink_test/CMakeLists.txt +++ b/source/tests/dynlink_test/CMakeLists.txt @@ -84,7 +84,9 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::dynlink ) @@ -95,6 +97,8 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + + $<$,$>:DYNLINK_TEST_MOCK_LOADER> ) # @@ -106,13 +110,25 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} + + $<$,$>:-Wl,-export_dynamic> + $<$:-rdynamic> ) # @@ -123,6 +139,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +if(OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_MOCK) + add_dependencies(${target} + mock_loader + ) +endif() + # # Define test labels # diff --git a/source/tests/dynlink_test/source/dynlink_test.cpp b/source/tests/dynlink_test/source/dynlink_test.cpp index 2f462dc90f..0119510cab 100644 --- a/source/tests/dynlink_test/source/dynlink_test.cpp +++ b/source/tests/dynlink_test/source/dynlink_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -28,65 +28,154 @@ #define DYNLINK_TEST_LIBRARY_PATH "DYNLINK_TEST_LIBRARY_PATH" -typedef void (*mock_loader_print_func)(void); +typedef const char *(*mock_loader_print_func)(void); class dynlink_test : public testing::Test { - protected: +protected: }; +#ifdef _WIN32 + #define EXPORT_SYMBOL __declspec(dllexport) +#else + #define EXPORT_SYMBOL __attribute__((visibility("default"))) +#endif + +extern "C" EXPORT_SYMBOL int function_from_current_executable(void) +{ + log_write("metacall", LOG_LEVEL_INFO, "function_from_current_executable"); + + return 48; +} + TEST_F(dynlink_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); dynlink_print_info(); log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object extension: %s", dynlink_extension()); + /* Test loading symbols from current process */ { - #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) - const char library_name[] = "mock_loaderd"; - #else - const char library_name[] = "mock_loader"; - #endif + dynlink proc = dynlink_load_self(DYNLINK_FLAGS_BIND_LAZY | DYNLINK_FLAGS_BIND_GLOBAL); - char * path = environment_variable_path_create(DYNLINK_TEST_LIBRARY_PATH, NULL); + ASSERT_NE((dynlink)proc, (dynlink)(NULL)); - dynlink handle = dynlink_load(path, library_name, DYNLINK_FLAGS_BIND_NOW | DYNLINK_FLAGS_BIND_GLOBAL); + dynlink_symbol_addr addr; - environment_variable_path_destroy(path); + EXPECT_EQ((int)0, dynlink_symbol(proc, "function_from_current_executable", &addr)); + + ASSERT_NE((dynlink_symbol_addr)addr, (dynlink_symbol_addr)NULL); + + int (*fn_ptr)(void) = (int (*)(void))addr; + + EXPECT_EQ((int)48, fn_ptr()); + + EXPECT_EQ((int (*)(void))(&function_from_current_executable), (int (*)(void))fn_ptr); + + dynlink_unload(proc); /* Should do nothing except by freeing the handle */ + } + +#ifdef DYNLINK_TEST_MOCK_LOADER + { + #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) + const char library_name[] = "mock_loaderd"; + #else + const char library_name[] = "mock_loader"; + #endif - EXPECT_NE(handle, (dynlink) NULL); + char *path = environment_variable_path_create(DYNLINK_TEST_LIBRARY_PATH, NULL, 0, NULL); - log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file: %s", dynlink_get_name_impl(handle)); + ASSERT_NE((char *)path, (char *)NULL); - if (handle != NULL) + /* Test library loading */ { - static dynlink_symbol_addr mock_loader_print_info_addr; + dynlink handle = dynlink_load(path, library_name, DYNLINK_FLAGS_BIND_NOW | DYNLINK_FLAGS_BIND_GLOBAL); - EXPECT_EQ((int) 0, dynlink_symbol(handle, DYNLINK_SYMBOL_STR("mock_loader_print_info"), &mock_loader_print_info_addr)); + ASSERT_NE(handle, (dynlink)NULL); - if (mock_loader_print_info_addr != NULL) - { - mock_loader_print_func print = DYNLINK_SYMBOL_GET(mock_loader_print_info_addr); + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file: %s", dynlink_get_path(handle)); + + EXPECT_STREQ(library_name, dynlink_get_name(handle)); - log_write("metacall", LOG_LEVEL_DEBUG, "Print function: %p", (void *)print); + if (handle != NULL) + { + dynlink_symbol_addr mock_loader_print_info_addr; - log_write("metacall", LOG_LEVEL_DEBUG, "Symbol pointer: %p", (void *)mock_loader_print_info_addr); + EXPECT_EQ((int)0, dynlink_symbol(handle, "mock_loader_print_info", &mock_loader_print_info_addr)); - if (DYNLINK_SYMBOL_GET(mock_loader_print_info_addr) != NULL) + if (mock_loader_print_info_addr != NULL) { - log_write("metacall", LOG_LEVEL_DEBUG, "Pointer is valid"); + mock_loader_print_func print = (mock_loader_print_func)mock_loader_print_info_addr; + + log_write("metacall", LOG_LEVEL_DEBUG, "Print function: %p", (void *)print); + + log_write("metacall", LOG_LEVEL_DEBUG, "Symbol pointer: %p", (void *)mock_loader_print_info_addr); + + if (mock_loader_print_info_addr != NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "Pointer is valid"); + } + + log_write("metacall", LOG_LEVEL_DEBUG, "Print: %s", print()); } - print(); + dynlink_unload(handle); } + } - dynlink_unload(handle); + /* Test loading symbols from absolute path */ + { + char library_name_platform[PORTABILITY_PATH_SIZE]; + char absolute_path[PORTABILITY_PATH_SIZE]; + + dynlink_platform_name(library_name, library_name_platform); + + portability_path_join(path, strlen(path) + 1, library_name_platform, strlen(library_name_platform) + 1, absolute_path, PORTABILITY_PATH_SIZE); + + dynlink handle = dynlink_load_absolute(absolute_path, DYNLINK_FLAGS_BIND_NOW | DYNLINK_FLAGS_BIND_GLOBAL); + + ASSERT_NE(handle, (dynlink)NULL); + + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object absolute path: %s", absolute_path); + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file name: %s", dynlink_get_path(handle)); + log_write("metacall", LOG_LEVEL_DEBUG, "Dynamic linked shared object file: %s", dynlink_get_name(handle)); + + EXPECT_STREQ(absolute_path, dynlink_get_path(handle)); + EXPECT_STREQ(library_name, dynlink_get_name(handle)); + + if (handle != NULL) + { + dynlink_symbol_addr mock_loader_print_info_addr; + + EXPECT_EQ((int)0, dynlink_symbol(handle, "mock_loader_print_info", &mock_loader_print_info_addr)); + + if (mock_loader_print_info_addr != NULL) + { + mock_loader_print_func print = (mock_loader_print_func)mock_loader_print_info_addr; + + log_write("metacall", LOG_LEVEL_DEBUG, "Print function: %p", (void *)print); + + log_write("metacall", LOG_LEVEL_DEBUG, "Symbol pointer: %p", (void *)mock_loader_print_info_addr); + + if (mock_loader_print_info_addr != NULL) + { + log_write("metacall", LOG_LEVEL_DEBUG, "Pointer is valid"); + } + + log_write("metacall", LOG_LEVEL_DEBUG, "Print: %s", print()); + } + + dynlink_unload(handle); + } } + + environment_variable_path_destroy(path); } +#endif } diff --git a/source/tests/dynlink_test/source/main.cpp b/source/tests/dynlink_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/dynlink_test/source/main.cpp +++ b/source/tests/dynlink_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/environment_test/CMakeLists.txt b/source/tests/environment_test/CMakeLists.txt index a37700c27b..3472e06a8b 100644 --- a/source/tests/environment_test/CMakeLists.txt +++ b/source/tests/environment_test/CMakeLists.txt @@ -81,6 +81,7 @@ target_link_libraries(${target} GTest ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::environment ) @@ -102,11 +103,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/environment_test/source/environment_test.cpp b/source/tests/environment_test/source/environment_test.cpp index c9d12ca5a4..576dfb28aa 100644 --- a/source/tests/environment_test/source/environment_test.cpp +++ b/source/tests/environment_test/source/environment_test.cpp @@ -2,7 +2,7 @@ * Format Library by Parra Studios * A cross-platform library for supporting platform specific environment features. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -27,19 +27,19 @@ class environment_test : public testing::Test public: }; -#define ENVIRONMENT_TEST_VARIABLE_TEXT "ENV_TEXT" -#define ENVIRONMENT_TEST_VARIABLE_PATH "ENV_PATH" +#define ENVIRONMENT_TEST_VARIABLE_TEXT "ENV_TEXT" +#define ENVIRONMENT_TEST_VARIABLE_PATH "ENV_PATH" #define ENVIRONMENT_TEST_VARIABLE_PATH_SANITIZED "ENV_SAN" TEST_F(environment_test, variable_text) { static const char variable_text_name[] = ENVIRONMENT_TEST_VARIABLE_TEXT; - char * variable_text = environment_variable_create(variable_text_name, "default"); + char *variable_text = environment_variable_create(variable_text_name, "default"); - ASSERT_NE((const char *) NULL, (const char *) variable_text); + ASSERT_NE((const char *)NULL, (const char *)variable_text); - EXPECT_EQ((int) 0, (int) strcmp(variable_text, "abcd")); + EXPECT_STREQ(variable_text, "abcd"); environment_variable_destroy(variable_text); } @@ -48,11 +48,11 @@ TEST_F(environment_test, variable_text_default) { static const char variable_text_name[] = "UNKNOWN"; - char * variable_text = environment_variable_create(variable_text_name, "default"); + char *variable_text = environment_variable_create(variable_text_name, "default"); - ASSERT_NE((const char *) NULL, (const char *) variable_text); + ASSERT_NE((const char *)NULL, (const char *)variable_text); - EXPECT_EQ((int) 0, (int) strcmp(variable_text, "default")); + EXPECT_STREQ(variable_text, "default"); environment_variable_destroy(variable_text); } @@ -61,20 +61,20 @@ TEST_F(environment_test, variable_static) { static const char variable_text_name[] = ENVIRONMENT_TEST_VARIABLE_TEXT; - const char * variable_text_static = environment_variable_get(variable_text_name, "default"); + const char *variable_text_static = environment_variable_get(variable_text_name, "default"); - EXPECT_EQ((int) 0, (int) strcmp(variable_text_static, "abcd")); + EXPECT_STREQ(variable_text_static, "abcd"); } TEST_F(environment_test, variable_path) { static const char variable_path_name[] = ENVIRONMENT_TEST_VARIABLE_PATH; - char * variable_path = environment_variable_path_create(variable_path_name, "default_path"); + char *variable_path = environment_variable_path_create(variable_path_name, "default_path", sizeof("default_path"), NULL); - ASSERT_NE((const char *) NULL, (const char *) variable_path); - - EXPECT_EQ((int) 0, (int) strcmp(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR)); + ASSERT_NE((const char *)NULL, (const char *)variable_path); + + EXPECT_STREQ(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR); environment_variable_path_destroy(variable_path); } @@ -83,25 +83,24 @@ TEST_F(environment_test, variable_path_default) { static const char variable_path_name[] = "UNKNOWN"; - char * variable_path = environment_variable_path_create(variable_path_name, "default_path"); + char *variable_path = environment_variable_path_create(variable_path_name, "default_path", sizeof("default_path"), NULL); - ASSERT_NE((const char *) NULL, (const char *) variable_path); + ASSERT_NE((const char *)NULL, (const char *)variable_path); - EXPECT_EQ((int) 0, (int) strcmp(variable_path, "default_path" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR)); + EXPECT_STREQ(variable_path, "default_path" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR); environment_variable_path_destroy(variable_path); } - TEST_F(environment_test, variable_path_sanitized) { static const char variable_path_name[] = ENVIRONMENT_TEST_VARIABLE_PATH_SANITIZED; - char * variable_path = environment_variable_path_create(variable_path_name, "default_path"); + char *variable_path = environment_variable_path_create(variable_path_name, "default_path", sizeof("default_path"), NULL); - ASSERT_NE((const char *) NULL, (const char *) variable_path); + ASSERT_NE((const char *)NULL, (const char *)variable_path); - EXPECT_EQ((int) 0, (int) strcmp(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR)); + EXPECT_STREQ(variable_path, "abcd" ENVIRONMENT_VARIABLE_PATH_SEPARATOR_STR); environment_variable_path_destroy(variable_path); } diff --git a/source/tests/environment_test/source/main.cpp b/source/tests/environment_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/environment_test/source/main.cpp +++ b/source/tests/environment_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/file_loader_test/CMakeLists.txt b/source/tests/file_loader_test/CMakeLists.txt deleted file mode 100644 index f3ee8fb35c..0000000000 --- a/source/tests/file_loader_test/CMakeLists.txt +++ /dev/null @@ -1,155 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_FILE) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target file-loader-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/file_loader_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::configuration -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND $ -) - -# -# Create custom target for data -# - -add_custom_target(${target}-data ALL - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/data/favicon.ico ${LOADER_SCRIPT_PATH} -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/file_loader_test/source/file_loader_test.cpp b/source/tests/file_loader_test/source/file_loader_test.cpp deleted file mode 100644 index 7f5ab04d75..0000000000 --- a/source/tests/file_loader_test/source/file_loader_test.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class file_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(file_loader_test, DefaultConstructor) -{ - const loader_naming_tag tag = "file"; - - const loader_naming_path names[] = - { - "favicon.ico" - }; - - const size_t size = sizeof(names) / sizeof(names[0]); - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) loader_load_from_file(tag, names, size, NULL)); - - for (size_t index = 0; index < size; ++index) - { - void * handle = loader_get_handle(tag, names[index]); - - EXPECT_NE((void *) NULL, (void *) handle); - - EXPECT_EQ((int) 0, (int) loader_clear(handle)); - } - - EXPECT_EQ((int) 0, (int) loader_unload()); -} diff --git a/source/tests/file_loader_test/source/main.cpp b/source/tests/file_loader_test/source/main.cpp deleted file mode 100644 index cdb6abc837..0000000000 --- a/source/tests/file_loader_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/loader_path_test/CMakeLists.txt b/source/tests/loader_path_test/CMakeLists.txt deleted file mode 100644 index 4eaa0cf413..0000000000 --- a/source/tests/loader_path_test/CMakeLists.txt +++ /dev/null @@ -1,133 +0,0 @@ -# -# Executable name and options -# - -# Target name -set(target loader-path-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/loader_path_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test labels -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) diff --git a/source/tests/loader_path_test/source/loader_path_test.cpp b/source/tests/loader_path_test/source/loader_path_test.cpp deleted file mode 100644 index 0b807fa216..0000000000 --- a/source/tests/loader_path_test/source/loader_path_test.cpp +++ /dev/null @@ -1,432 +0,0 @@ -/* -* Loader Library by Parra Studios -* Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -* -* A library for loading executable code at run-time into a process. -* -*/ - -#include - -#include - -#include - -class loader_path_test : public testing::Test -{ - public: -}; - -TEST_F(loader_path_test, loader_path_test_get_path_of_path) -{ - const char base[] = "/a/b/c/"; - const char result[] = "/a/b/c/"; - - loader_naming_path path; - - size_t size = loader_path_get_path(base, sizeof(base), path); - - EXPECT_EQ((int) 0, (int) strcmp(path, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_get_path_of_filepath) -{ - const char base[] = "/a/b/c/asd"; - const char result[] = "/a/b/c/"; - - loader_naming_path path; - - size_t size = loader_path_get_path(base, sizeof(base), path); - - EXPECT_EQ((int)0, (int)strcmp(path, result)); - EXPECT_EQ((size_t)size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char)result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_none_slash) -{ - const char left[] = "/a/b/c"; - const char right[] = "e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_left_slash) -{ - const char left[] = "/a/b/c/"; - const char right[] = "e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_right_slash) -{ - const char left[] = "/a/b/c"; - const char right[] = "/e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_both_slash) -{ - const char left[] = "/a/b/c/"; - const char right[] = "/e/f/g/"; - const char result[] = "/a/b/c/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_left_empty) -{ - const char left[] = ""; - const char right[] = "/e/f/g/"; - const char result[] = "/e/f/g/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_right_empty) -{ - const char left[] = "/a/b/c/"; - const char right[] = ""; - const char result[] = "/a/b/c/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_right_empty_non_slash) -{ - const char left[] = "/a/b/c"; - const char right[] = ""; - const char result[] = "/a/b/c/"; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_join_both_empty) -{ - const char left[] = ""; - const char right[] = ""; - const char result[] = ""; - - loader_naming_path join; - - size_t size = loader_path_join(left, sizeof(left), right, sizeof(right), join); - - EXPECT_EQ((int) 0, (int) strcmp(join, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_dot) -{ - const char path[] = "./a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_double_dot) -{ - const char path[] = "../a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_many_dot) -{ - const char path[] = "./././././a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_many_double_dot) -{ - const char path[] = "../../../../../a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_dot_non_slash) -{ - const char path[] = ".a/b/c"; - const char result[] = ".a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_many_dot_non_slash) -{ - const char path[] = "..a/b/c"; - const char result[] = "..a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_begin_invalid) -{ - const char path[] = "..././.../...../a/b/c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_double_dot) -{ - const char path[] = "../a/b/../c"; - const char result[] = "a/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_double_dot_all) -{ - const char path[] = "../a/b/../../c"; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_double_dot_break) -{ - const char path[] = "../a/b/../../../c"; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_dot) -{ - const char path[] = "../a/./././b/./././c"; - const char result[] = "a/b/c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_middle_mixed_dot) -{ - const char path[] = "../a/./././../b/././.././../c"; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_end_dot) -{ - const char path[] = "../a/./././../b/././.././../c/."; - const char result[] = "c"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_end_double_dot) -{ - const char path[] = "../a/./././b/././../c/.."; - const char result[] = "a"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_end_mixed_dot) -{ - const char path[] = "../a/b/c/.././../."; - const char result[] = "a"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_absolute_end_mixed_dot) -{ - const char path[] = "/a/b/c/../../../."; - const char result[] = "/"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_absolute_end_dot) -{ - const char path[] = "/."; - const char result[] = "/"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_relative_begin_end_dot) -{ - const char path[] = "./."; - const char result[] = "."; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} - -TEST_F(loader_path_test, loader_path_test_canonical_absolute_end_many_dot) -{ - const char path[] = "/./././"; - const char result[] = "/"; - - loader_naming_path canonical; - - size_t size = loader_path_canonical(path, sizeof(path), canonical); - - EXPECT_EQ((int) 0, (int) strcmp(canonical, result)); - EXPECT_EQ((size_t) size, (size_t) sizeof(result)); - EXPECT_EQ((char) '\0', (char) result[size - 1]); -} diff --git a/source/tests/loader_path_test/source/main.cpp b/source/tests/loader_path_test/source/main.cpp deleted file mode 100644 index 09764b62ba..0000000000 --- a/source/tests/loader_path_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Abstract Data Type Library by Parra Studios - * A abstract data type library providing generic containers. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/log_custom_test/CMakeLists.txt b/source/tests/log_custom_test/CMakeLists.txt new file mode 100644 index 0000000000..f46ff859e2 --- /dev/null +++ b/source/tests/log_custom_test/CMakeLists.txt @@ -0,0 +1,140 @@ +# +# Executable name and options +# + +# Target name +set(target log-custom-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/log_custom_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::version + ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading + ${META_PROJECT_NAME}::log +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/log_custom_test/source/log_custom_test.cpp b/source/tests/log_custom_test/source/log_custom_test.cpp new file mode 100644 index 0000000000..f97ebcc6b6 --- /dev/null +++ b/source/tests/log_custom_test/source/log_custom_test.cpp @@ -0,0 +1,142 @@ +/* + * Logger Library by Parra Studios + * A generic logger library providing application execution reports. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include + +static const char format[] = "%.19s #%" PRIu64 " %s:%" PRIuS " %s @%s "; + +class log_custom_test : public testing::Test +{ +public: +}; + +size_t format_size(void *context, const char *time, uint64_t id, size_t line, const char *func, const char *file, const char *level, const char *message, log_policy_format_custom_va_list args) +{ + size_t length = 0; + + (void)context; + + if (args != NULL) + { + va_list va; + + va_copy(va, args->va); + + length = vsnprintf(NULL, 0, message, va); + + va_end(va); + } + else + { + length = strlen(message); + } + + return snprintf(NULL, 0, format, time, id, file, line, func, level) + length + 1; +} + +size_t format_serialize(void *context, void *buffer, const size_t size, const char *time, uint64_t id, size_t line, const char *func, const char *file, const char *level, const char *message, log_policy_format_custom_va_list args) +{ + size_t length = snprintf((char *)buffer, size, format, time, id, file, line, func, level); + char *body = &(((char *)buffer)[length]); + + (void)context; + + if (args != NULL) + { + va_list va; + + va_copy(va, args->va); + + length += vsnprintf(body, size - length, message, va); + + va_end(va); + } + else + { + length += snprintf(body, size - length, "%s", message); + } + + return length + 1; +} + +size_t format_deserialize(void *context, const void *buffer, const size_t size, const char *time, uint64_t id, size_t line, const char *func, const char *file, const char *level, const char *message, log_policy_format_custom_va_list args) +{ + /* TODO */ + (void)context; + (void)buffer; + (void)time; + (void)id; + (void)line; + (void)func; + (void)file; + (void)level; + (void)message; + (void)args; + + return size; +} + +int stream_flush(void *context) +{ + (void)context; + + fflush(stdout); + + return 0; +} + +int stream_write(void *context, const char *buffer, const size_t size) +{ + (void)context; + (void)size; + + printf("%s\n", buffer); + + return 0; +} + +TEST_F(log_custom_test, DefaultConstructor) +{ + const char name[] = "custom_log"; + + /* Create logs */ + EXPECT_EQ((int)0, (int)log_create(name)); + + /* Set policies */ + EXPECT_EQ((int)0, (int)log_configure(name, + log_policy_format_custom(NULL, &format_size, &format_serialize, &format_deserialize), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_custom(NULL, &stream_write, &stream_flush))); + + /* Write simple logs */ + EXPECT_EQ((int)0, (int)log_write(name, LOG_LEVEL_INFO, "hello world")); + + /* Write varidic log */ + EXPECT_EQ((int)0, (int)log_write(name, LOG_LEVEL_INFO, "hello world from log %d", 20)); + + /* Clear log */ + EXPECT_EQ((int)0, (int)log_clear(name)); +} diff --git a/source/tests/log_custom_test/source/main.cpp b/source/tests/log_custom_test/source/main.cpp new file mode 100644 index 0000000000..ccd0ee35a6 --- /dev/null +++ b/source/tests/log_custom_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Logger Library by Parra Studios + * A generic logger library providing application execution reports. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/log_test/CMakeLists.txt b/source/tests/log_test/CMakeLists.txt index 83747b931c..d091de149f 100644 --- a/source/tests/log_test/CMakeLists.txt +++ b/source/tests/log_test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log ) @@ -104,11 +105,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/log_test/source/log_test.cpp b/source/tests/log_test/source/log_test.cpp index 1aa1738044..3e1121bc7a 100644 --- a/source/tests/log_test/source/log_test.cpp +++ b/source/tests/log_test/source/log_test.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,37 +18,35 @@ * */ -#include +#include #include -#include #include +#include #include class log_test : public testing::Test { - public: +public: }; TEST_F(log_test, DefaultConstructor) { struct log_name_list_type { - const char * name; + const char *name; unsigned int value; - } - log_name_list[] = - { - { "test_log_text_sync_seq_stdout", 0 }, - { "test_log_text_sync_seq_stderr", 1 }, - { "test_log_text_sync_seq_file", 2 }, - { "test_log_text_sync_seq_syslog", 3 }, - { "test_log_text_sync_seq_socket", 4 }, - { "test_log_bin_async_bat_stdout", 5 }, - { "test_log_bin_async_bat_stderr", 6 }, - { "test_log_bin_async_bat_file", 7 }, - { "test_log_bin_async_bat_syslog", 8 }, - { "test_log_bin_async_bat_socket", 9 } + } log_name_list[] = { + { "test_log_text_sync_seq_stdout", 0 }, + { "test_log_text_sync_seq_stderr", 1 }, + { "test_log_text_sync_seq_file", 2 }, + { "test_log_text_sync_seq_syslog", 3 }, + { "test_log_text_sync_seq_socket", 4 }, + { "test_log_bin_async_bat_stdout", 5 }, + { "test_log_bin_async_bat_stderr", 6 }, + { "test_log_bin_async_bat_file", 7 }, + { "test_log_bin_async_bat_syslog", 8 }, + { "test_log_bin_async_bat_socket", 9 } }; const size_t log_name_list_size = sizeof(log_name_list) / sizeof(log_name_list[0]); @@ -61,36 +59,36 @@ TEST_F(log_test, DefaultConstructor) log_map map = log_map_create((size_t)0x00000200); - EXPECT_NE((log_map) NULL, (log_map) map); + EXPECT_NE((log_map)NULL, (log_map)map); for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_map_insert(map, log_name_list[iterator].name, (const void *)&log_name_list[iterator].value)); + EXPECT_EQ((int)0, (int)log_map_insert(map, log_name_list[iterator].name, (const void *)&log_name_list[iterator].value)); } - EXPECT_EQ((size_t) log_name_list_size, (size_t) log_map_size(map)); + EXPECT_EQ((size_t)log_name_list_size, (size_t)log_map_size(map)); for (iterator = 0; iterator < log_name_list_size; ++iterator) { - const void * value_ptr = log_map_get(map, log_name_list[iterator].name); + const void *value_ptr = log_map_get(map, log_name_list[iterator].name); unsigned int value = *((unsigned int *)value_ptr); - EXPECT_EQ((unsigned int) log_name_list[iterator].value, (unsigned int) value); + EXPECT_EQ((unsigned int)log_name_list[iterator].value, (unsigned int)value); } for (map_iterator = log_map_iterator_begin(map); log_map_iterator_end(map_iterator) != 0; log_map_iterator_next(map_iterator)) { - const char * key = log_map_iterator_key(map_iterator); + const char *key = log_map_iterator_key(map_iterator); - const void * value_ptr = log_map_iterator_value(map_iterator); + const void *value_ptr = log_map_iterator_value(map_iterator); unsigned int value = *((unsigned int *)value_ptr); - EXPECT_EQ((int) 0, (int) strcmp(log_name_list[value].name, key)); + EXPECT_STREQ(log_name_list[value].name, key); } - EXPECT_EQ((int) log_map_destroy(map), (int) 0); + EXPECT_EQ((int)log_map_destroy(map), (int)0); } /* Create logs */ @@ -99,7 +97,7 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_create(log_name_list[iterator].name)); + EXPECT_EQ((int)0, (int)log_create(log_name_list[iterator].name)); } } @@ -107,66 +105,65 @@ TEST_F(log_test, DefaultConstructor) { const size_t storage_batch_size = ((size_t)0x00000010); - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[0].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[1].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stderr))); - - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[2].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_file(log_name_list[2].name, "a+"))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[3].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_syslog(log_name_list[3].name))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[4].name, - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_socket("127.0.0.1", UINT16_C(0x0208)))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[5].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[6].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_stdio(stderr))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[7].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_file(log_name_list[7].name, "a+"))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[8].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_syslog(log_name_list[8].name))); - - EXPECT_EQ((int) 0, (int) log_configure(log_name_list[9].name, - log_policy_format_binary(), - log_policy_schedule_async(), - log_policy_storage_batch(storage_batch_size), - log_policy_stream_socket("127.0.0.1", UINT16_C(0x0209)))); + EXPECT_EQ((int)0, (int)log_configure(log_name_list[0].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[1].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stderr))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[2].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_file(log_name_list[2].name, "a+"))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[3].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_syslog(log_name_list[3].name))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[4].name, + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_socket("127.0.0.1", UINT16_C(0x0208)))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[5].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[6].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_stdio(stderr))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[7].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_file(log_name_list[7].name, "a+"))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[8].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_syslog(log_name_list[8].name))); + + EXPECT_EQ((int)0, (int)log_configure(log_name_list[9].name, + log_policy_format_binary(), + log_policy_schedule_async(), + log_policy_storage_batch(storage_batch_size), + log_policy_stream_socket("127.0.0.1", UINT16_C(0x0209)))); } /* Write simple logs */ @@ -175,8 +172,8 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world")); - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hell yeah")); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world")); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hell yeah")); } } @@ -186,9 +183,9 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", iterator)); - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "%" PRIuS " %f", iterator + 1, 34.02346)); - EXPECT_EQ((int) 0, (int) log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "double log (id : %" PRIuS ")", iterator * 2)); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", iterator)); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "%" PRIuS " %f", iterator + 1, 34.02346)); + EXPECT_EQ((int)0, (int)log_write(log_name_list[iterator].name, LOG_LEVEL_INFO, "double log (id : %" PRIuS ")", iterator * 2)); } } @@ -198,35 +195,35 @@ TEST_F(log_test, DefaultConstructor) for (iterator = 0; iterator < log_name_list_size; ++iterator) { - EXPECT_EQ((int) 0, (int) log_clear(log_name_list[iterator].name)); + EXPECT_EQ((int)0, (int)log_clear(log_name_list[iterator].name)); } } /* Policy format text flags */ { - EXPECT_EQ((int) 0, (int) log_configure("newline", - log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_NEWLINE), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "NEW LINE A")); - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "NEW LINE B")); - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); - EXPECT_EQ((int) 0, (int) log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); - - EXPECT_EQ((int) 0, (int) log_configure("nonewline", - log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_EMPTY), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE A")); - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE B")); - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); - EXPECT_EQ((int) 0, (int) log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); - - EXPECT_EQ((int) 0, (int) log_clear("newline")); - EXPECT_EQ((int) 0, (int) log_clear("nonewline")); + EXPECT_EQ((int)0, (int)log_configure("newline", + log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_NEWLINE), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "NEW LINE A")); + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "NEW LINE B")); + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); + EXPECT_EQ((int)0, (int)log_write("newline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); + + EXPECT_EQ((int)0, (int)log_configure("nonewline", + log_policy_format_text_flags(LOG_POLICY_FORMAT_TEXT_EMPTY), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE A")); + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "NO NEW LINE B")); + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 1234)); + EXPECT_EQ((int)0, (int)log_write("nonewline", LOG_LEVEL_INFO, "hello world from log (id : %" PRIuS ")", 5432)); + + EXPECT_EQ((int)0, (int)log_clear("newline")); + EXPECT_EQ((int)0, (int)log_clear("nonewline")); } } diff --git a/source/tests/log_test/source/main.cpp b/source/tests/log_test/source/main.cpp index 8f29968341..ccd0ee35a6 100644 --- a/source/tests/log_test/source/main.cpp +++ b/source/tests/log_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/memcheck/valgrind-dl.supp b/source/tests/memcheck/valgrind-dl.supp new file mode 100644 index 0000000000..706cc1bbdf --- /dev/null +++ b/source/tests/memcheck/valgrind-dl.supp @@ -0,0 +1,25 @@ +# False positives from libdl from GLIBC + +{ + Ignore dlopen allocation memory leak from GLIB. + Memcheck:Leak + fun:?alloc + ... + fun:do_dlopen +} + +{ + Ignore dlopen allocation memory leak from GLIB. + Memcheck:Leak + fun:?alloc + ... + fun:dlopen* +} + +{ + Ignore dlclose allocation memory leak from GLIB. + Memcheck:Leak + fun:malloc + ... + fun:dlclose +} diff --git a/source/tests/memcheck/valgrind-node.supp b/source/tests/memcheck/valgrind-node.supp new file mode 100644 index 0000000000..30360f9018 --- /dev/null +++ b/source/tests/memcheck/valgrind-node.supp @@ -0,0 +1,469 @@ +{ + + Helgrind:Race + ... + fun:_ZN4node12_GLOBAL__N_1L20PlatformWorkerThreadEPv + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v88platform7tracing17TracingController31GetCategoryGroupEnabledInternalEPKc + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v86Locker8IsActiveEv + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v86Locker10InitializeEPNS_7IsolateE + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v88internal7Isolate15SetEmbeddedBlobEPKhj + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v88internal4Heap14SetStackLimitsEv + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v88internal7Isolate23CurrentEmbeddedBlobSizeEv + ... +} +{ + + Helgrind:Race + ... + fun:_ZN2v88internal7Isolate19CurrentEmbeddedBlobEv + ... +} +{ + + Memcheck:Param + epoll_ctl(event) + fun:epoll_ctl + fun:uv__io_poll + ... + obj:*libnode* +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + fun:realloc + ... + fun:uv__tcp_connect + fun:uv_tcp_connect + ... + fun:uv__async_io + fun:uv__io_poll + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + fun:realloc + ... + fun:uv__tcp_listen + fun:uv_listen + ... + fun:uv__async_io + fun:uv__io_poll + ... +} +{ + + Memcheck:Cond + ... + fun:_ZN2v88internal9ExitFrame23GetStateForFramePointerEmPNS0_10StackFrame5StateE + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:pthread_create* + fun:_ZN4node9inspector12_GLOBAL__N_1L23StartDebugSignalHandlerEv + ... +} +{ + + Memcheck:Param + ioctl(generic) + fun:* + fun:uv__fs_copyfile + ... +} +{ + + Memcheck:Param + epoll_pwait(sigmask) + fun:* + fun:uv__io_poll + ... +} +{ + + Helgrind:Race + ... + fun:uv_loop_close + fun:_ZN4node18CheckedUvLoopCloseEP9uv_loop_s + fun:_ZZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler5StartEvENUlPvE_4_FUNES2_ + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:allocate_dtv + fun:_dl_allocate_tls + fun:allocate_stack + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v87tracing23TracingCategoryObserver5SetUpEv + fun:_ZN4node24InitializeOncePerProcessEiPPc + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal20CodeRangeAddressHint20NotifyFreedCodeRangeEmm + fun:_ZN2v88internal15MemoryAllocator8TearDownEv + fun:_ZN2v88internal4Heap8TearDownEv + fun:_ZN2v88internal7Isolate6DeinitEv + fun:_ZN2v88internal7Isolate6DeleteEPS1_ + fun:_ZN4node16NodeMainInstanceD1Ev + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node8Metadata8Versions22InitializeIntlVersionsEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node22PerIsolatePlatformData28FlushForegroundTasksInternalEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:uv_loop_init + fun:uv_default_loop + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node22PerIsolatePlatformData8ShutdownEv + fun:_ZN4node12NodePlatform17UnregisterIsolateEPN2v87IsolateE + fun:_ZN4node16NodeMainInstanceD1Ev + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node22PerIsolatePlatformDataC1EPN2v87IsolateEP9uv_loop_s + fun:_ZN4node12NodePlatform15RegisterIsolateEPN2v87IsolateEP9uv_loop_s + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node12NodePlatform15RegisterIsolateEPN2v87IsolateEP9uv_loop_s + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal14AllocWithRetryEm + fun:_ZN2v88internal19AccountingAllocator15AllocateSegmentEm + fun:_ZN2v88internal4Zone9NewExpandEm + fun:_ZN2v88internal8compiler4Type5RangeEddPNS0_4ZoneE + fun:_ZN2v88internal8compiler9TypeCacheC1Ev + fun:_ZN2v88internal8compiler9TypeCache3GetEv + fun:_ZN2v88internal8compiler29JSNativeContextSpecializationC1EPNS1_15AdvancedReducer6EditorEPNS1_7JSGraphEPNS1_12JSHeapBrokerENS_4base5FlagsINS2_4FlagEiEEPNS1_23CompilationDependenciesEPNS0_4ZoneESH_ + fun:_ZN2v88internal8compiler12PipelineImpl11CreateGraphEv + fun:_ZN2v88internal8compiler22PipelineCompilationJob14PrepareJobImplEPNS0_7IsolateE + fun:_ZN2v88internal23OptimizedCompilationJob10PrepareJobEPNS0_7IsolateE + ... +} +{ + + Memcheck:Leak + ... + fun:napi_module_register + ... +} +{ + + Memcheck:Cond + fun:_ZNK4node11Environment16can_call_into_jsEv + fun:_ZN4node10AsyncHooks20clear_async_id_stackEv + fun:_ZN4node10AsyncHooksC1EPN2v87IsolateEPKNS0_13SerializeInfoE + ... + fun:_ZN4node16NodeMainInstance21CreateMainEnvironmentEPi + fun:_ZN4node16NodeMainInstance3RunEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal4HeapD1Ev + fun:_ZN2v88internal7IsolateD1Ev + fun:_ZN2v88internal7Isolate6DeleteEPS1_ + fun:_ZN4node16NodeMainInstanceD1Ev + fun:_ZN4node22LoadSnapshotDataAndRunEPPKNS_12SnapshotDataEPKNS_20InitializationResultE + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN6icu_7227locale_set_default_internalEPKcR10UErrorCode + fun:_ZN6icu_726Locale10getDefaultEv + fun:_ZN6icu_726Locale4initEPKca + fun:_ZN6icu_726LocaleC1Ev + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:make_unique + fun:ScheduleTimer + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + fun:_ZN2v88internal13MemoryReducer11NotifyTimerERKNS1_5EventE + fun:_ZN2v88internal13MemoryReducer9TimerTask11RunInternalEv + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskESt10unique_ptrIN2v84TaskESt14default_deleteIS3_EE + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskEP10uv_timer_s + fun:uv__run_timers + fun:uv_run + fun:_ZN4node13SpinEventLoopEPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEPiPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEv + fun:_ZN4node22LoadSnapshotDataAndRunEPPKNS_12SnapshotDataEPKNS_20InitializationResultE + fun:_ZN4node5StartEiPPc + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:_ZN4node22PerIsolatePlatformData15PostDelayedTaskESt10unique_ptrIN2v84TaskESt14default_deleteIS3_EEd + fun:ScheduleTimer + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + fun:_ZN2v88internal13MemoryReducer11NotifyTimerERKNS1_5EventE + fun:_ZN2v88internal13MemoryReducer9TimerTask11RunInternalEv + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskESt10unique_ptrIN2v84TaskESt14default_deleteIS3_EE + fun:_ZN4node22PerIsolatePlatformData17RunForegroundTaskEP10uv_timer_s + fun:uv__run_timers + fun:uv_run + fun:_ZN4node13SpinEventLoopEPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEPiPNS_11EnvironmentE + fun:_ZN4node16NodeMainInstance3RunEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node9TaskQueueINS_11DelayedTaskEE6PopAllEv + fun:_ZN4node22PerIsolatePlatformData8ShutdownEv + fun:_ZN4node12NodePlatform17UnregisterIsolateEPN2v87IsolateE + fun:_ZN4node16NodeMainInstanceD1Ev + fun:_ZN4node22LoadSnapshotDataAndRunEPPKNS_12SnapshotDataEPKNS_20InitializationResultE + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN4node9TaskQueueIN2v84TaskEE6PopAllEv + fun:_ZN4node22PerIsolatePlatformData8ShutdownEv + fun:_ZN4node12NodePlatform17UnregisterIsolateEPN2v87IsolateE + fun:_ZN4node16NodeMainInstanceD1Ev + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal12_GLOBAL__N_133InitializeSharedReadOnlyArtifactsEv + fun:_ZN2v88internal12ReadOnlyHeap5SetUpEPNS0_7IsolateEPNS0_12SnapshotDataEb + fun:_ZN2v88internal7Isolate4InitEPNS0_12SnapshotDataES3_S3_b + fun:Initialize + fun:_ZN2v88internal8Snapshot10InitializeEPNS0_7IsolateE + fun:_ZN2v87Isolate10InitializeEPS0_RKNS0_12CreateParamsE + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:uprv_realloc_72 + fun:_ZN6icu_7210UnicodeSet7compactEv + fun:_ZN6icu_7210UnicodeSet6freezeEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:uprv_malloc_72 + fun:_ZN6icu_727UMemorynwEm + fun:_ZN6icu_7210UnicodeSet6freezeEv + ... +} +{ + + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_ZN2v88internal14AllocWithRetryEmPFPvmE + fun:_ZN2v88internal19AccountingAllocator15AllocateSegmentEmb + fun:_ZN2v88internal4Zone9NewExpandEm + fun:Allocate + fun:New + fun:New + fun:New + fun:_ZN2v88internal8compiler4Type5RangeEddPNS0_4ZoneE + fun:CreateRange + fun:CreateRange + fun:_ZN2v88internal8compiler9TypeCacheC1Ev + fun:LeakyObject<> + fun:_ZN2v88internal8compiler9TypeCache3GetEv + fun:_ZN2v88internal8compiler29JSNativeContextSpecializationC1EPNS1_15AdvancedReducer6EditorEPNS1_7JSGraphEPNS1_12JSHeapBrokerENS_4base5FlagsINS2_4FlagEiEEPNS1_23CompilationDependenciesEPNS0_4ZoneESH_ + fun:_ZN2v88internal8compiler13InliningPhase3RunEPNS1_12PipelineDataEPNS0_4ZoneE.isra.0 + fun:Run + fun:_ZN2v88internal8compiler12PipelineImpl11CreateGraphEv + fun:_ZN2v88internal8compiler22PipelineCompilationJob14ExecuteJobImplEPNS0_16RuntimeCallStatsEPNS0_12LocalIsolateE + fun:_ZN2v88internal23OptimizedCompilationJob10ExecuteJobEPNS0_16RuntimeCallStatsEPNS0_12LocalIsolateE + fun:_ZN2v88internal27OptimizingCompileDispatcher11CompileNextEPNS0_22TurbofanCompilationJobEPNS0_12LocalIsolateE + fun:_ZThn32_N2v88internal14CancelableTask3RunEv + fun:_ZN4node12_GLOBAL__N_1L20PlatformWorkerThreadEPv + ... +} +{ + + Helgrind:Race + ... + fun:store + fun:atomic_store_explicit + fun:Release_Store + fun:GetCategoryGroupEnabled + fun:_ZN2v88platform7tracing17TracingController23GetCategoryGroupEnabledEPKc + ... +} +{ + + Helgrind:Race + ... + fun:uv_loop_close + fun:_ZN4node18CheckedUvLoopCloseEP9uv_loop_s + fun:_ZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler3RunEv + fun:_ZZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler5StartEvENKUlPvE_clES2_ + fun:_ZZN4node23WorkerThreadsTaskRunner20DelayedTaskScheduler5StartEvENUlPvE_4_FUNES2_ + ... +} +{ + + Memcheck:Leak + match-leak-kinds: possible + ... + fun:ScheduleTimer + fun:_ZN2v88internal13MemoryReducer13ScheduleTimerEd + fun:_ZN2v88internal4Heap28NotifyOldGenerationExpansionENS0_15AllocationSpaceEPNS0_11MemoryChunkE + fun:_ZN2v88internal10PagedSpace9TryExpandEiNS0_16AllocationOriginE + fun:_ZN2v88internal10PagedSpace16RawRefillLabMainEiNS0_16AllocationOriginE + fun:_ZN2v88internal10PagedSpace13RefillLabMainEiNS0_16AllocationOriginE + fun:_ZN2v88internal19SpaceWithLinearArea20AllocateRawUnalignedEiNS0_16AllocationOriginE + fun:AllocateRawSlow + fun:AllocateRaw + fun:_ZN2v88internal13HeapAllocator11AllocateRawILNS0_14AllocationTypeE1EEENS0_16AllocationResultEiNS0_16AllocationOriginENS0_19AllocationAlignmentE + fun:AllocateRawWith<(v8::internal::HeapAllocator::AllocationRetryMode)1> + fun:_ZN2v88internal7Factory11AllocateRawEiNS0_14AllocationTypeENS0_19AllocationAlignmentE + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE26AllocateRawWithImmortalMapEiNS0_14AllocationTypeENS0_3MapENS0_19AllocationAlignmentE + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE21NewSharedFunctionInfoEv + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE21NewSharedFunctionInfoENS0_11MaybeHandleINS0_6StringEEENS4_INS0_10HeapObjectEEENS0_7BuiltinENS0_12FunctionKindE + fun:_ZN2v88internal11FactoryBaseINS0_7FactoryEE31NewSharedFunctionInfoForLiteralEPNS0_15FunctionLiteralENS0_6HandleINS0_6ScriptEEEb + fun:_ZN2v88internal11interpreter17BytecodeGenerator25AllocateDeferredConstantsINS0_7IsolateEEEvPT_NS0_6HandleINS0_6ScriptEEE + fun:_ZN2v88internal11interpreter17BytecodeGenerator16FinalizeBytecodeINS0_7IsolateEEENS0_6HandleINS0_13BytecodeArrayEEEPT_NS5_INS0_6ScriptEEE + fun:_ZN2v88internal11interpreter25InterpreterCompilationJob17DoFinalizeJobImplINS0_7IsolateEEENS0_14CompilationJob6StatusENS0_6HandleINS0_18SharedFunctionInfoEEEPT_ + fun:_ZN2v88internal11interpreter25InterpreterCompilationJob15FinalizeJobImplENS0_6HandleINS0_18SharedFunctionInfoEEEPNS0_7IsolateE + fun:_ZN2v88internal25UnoptimizedCompilationJob11FinalizeJobENS0_6HandleINS0_18SharedFunctionInfoEEEPNS0_7IsolateE + fun:_ZN2v88internal12_GLOBAL__N_139FinalizeSingleUnoptimizedCompilationJobINS0_7IsolateEEENS0_14CompilationJob6StatusEPNS0_25UnoptimizedCompilationJobENS0_6HandleINS0_18SharedFunctionInfoEEEPT_PSt6vectorINS0_34FinalizeUnoptimizedCompilationDataESaISE_EE + fun:_ZN2v88internal12_GLOBAL__N_155IterativelyExecuteAndFinalizeUnoptimizedCompilationJobsINS0_7IsolateEEEbPT_NS0_6HandleINS0_18SharedFunctionInfoEEENS6_INS0_6ScriptEEEPNS0_9ParseInfoEPNS0_19AccountingAllocatorEPNS0_15IsCompiledScopeEPSt6vectorINS0_34FinalizeUnoptimizedCompilationDataESaISI_EEPSH_INS0_27DeferredFinalizationJobDataESaISM_EE.constprop.0 + fun:_ZN2v88internal12_GLOBAL__N_115CompileToplevelEPNS0_9ParseInfoENS0_6HandleINS0_6ScriptEEENS0_11MaybeHandleINS0_9ScopeInfoEEEPNS0_7IsolateEPNS0_15IsCompiledScopeE + fun:_ZN2v88internal8Compiler18GetWrappedFunctionENS0_6HandleINS0_6StringEEENS2_INS0_10FixedArrayEEENS2_INS0_7ContextEEERKNS0_13ScriptDetailsEPNS0_17AlignedCachedDataENS_14ScriptCompiler14CompileOptionsENSE_13NoCacheReasonE + fun:_ZN2v814ScriptCompiler23CompileFunctionInternalENS_5LocalINS_7ContextEEEPNS0_6SourceEmPNS1_INS_6StringEEEmPNS1_INS_6ObjectEEENS0_14CompileOptionsENS0_13NoCacheReasonEPNS1_INS_14ScriptOrModuleEEE + fun:_ZN2v814ScriptCompiler15CompileFunctionENS_5LocalINS_7ContextEEEPNS0_6SourceEmPNS1_INS_6StringEEEmPNS1_INS_6ObjectEEENS0_14CompileOptionsENS0_13NoCacheReasonE + fun:_ZN4node8builtins13BuiltinLoader24LookupAndCompileInternalEN2v85LocalINS2_7ContextEEEPKcPSt6vectorINS3_INS2_6StringEEESaISA_EEPNS1_6ResultE + fun:_ZN4node8builtins13BuiltinLoader16LookupAndCompileEN2v85LocalINS2_7ContextEEEPKcPNS_11EnvironmentE + fun:_ZN4node8builtins13BuiltinLoader15CompileFunctionERKN2v820FunctionCallbackInfoINS2_5ValueEEE + ... +} diff --git a/source/tests/memcheck/valgrind-python.supp b/source/tests/memcheck/valgrind-python.supp index a3c90dea49..d7feeb8c18 100644 --- a/source/tests/memcheck/valgrind-python.supp +++ b/source/tests/memcheck/valgrind-python.supp @@ -1,4 +1,6 @@ # +# From: https://github.com/python/cpython/blob/master/Misc/valgrind-python.supp +# # This is a valgrind suppression file that should be used when using valgrind. # # Here's an example of running valgrind: @@ -124,41 +126,41 @@ fun:_dl_allocate_tls } -###{ -### ADDRESS_IN_RANGE/Invalid read of size 4 -### Memcheck:Addr4 -### fun:PyObject_Free -###} -### -###{ -### ADDRESS_IN_RANGE/Invalid read of size 4 -### Memcheck:Value4 -### fun:PyObject_Free -###} -### -###{ -### ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value -### Memcheck:Cond -### fun:PyObject_Free -###} +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:PyObject_Free +} -###{ -### ADDRESS_IN_RANGE/Invalid read of size 4 -### Memcheck:Addr4 -### fun:PyObject_Realloc -###} -### -###{ -### ADDRESS_IN_RANGE/Invalid read of size 4 -### Memcheck:Value4 -### fun:PyObject_Realloc -###} -### -###{ -### ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value -### Memcheck:Cond -### fun:PyObject_Realloc -###} +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:PyObject_Free +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Addr4 + fun:PyObject_Realloc +} + +{ + ADDRESS_IN_RANGE/Invalid read of size 4 + Memcheck:Value4 + fun:PyObject_Realloc +} + +{ + ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value + Memcheck:Cond + fun:PyObject_Realloc +} ### ### All the suppressions below are for errors that occur within libraries diff --git a/source/tests/memcheck/valgrind-ruby.supp b/source/tests/memcheck/valgrind-ruby.supp new file mode 100644 index 0000000000..934aac4943 --- /dev/null +++ b/source/tests/memcheck/valgrind-ruby.supp @@ -0,0 +1,220 @@ +{ + Init Ruby VM + Memcheck:Leak + ... + fun:ruby_setup + ... +} +{ + Ruby false positive eval_string_with_cref + Memcheck:Leak + ... + fun:eval_string_with_cref + ... +} +{ + Ruby false positive rb_add_method_cfunc + Memcheck:Leak + ... + fun:rb_add_method_cfunc + ... +} +{ + Ruby false positive rb_check_funcall + Memcheck:Leak + ... + fun:rb_check_funcall + ... +} +{ + Ruby false positive rb_class_boot: Called for all the different ways to create a Class + Memcheck:Leak + ... + fun:rb_class_boot + ... +} +{ + Ruby false positive rb_enc_raise + Memcheck:Leak + ... + fun:rb_enc_raise + ... +} +{ + Ruby false positive rb_exc_raise + Memcheck:Leak + ... + fun:rb_exc_raise + ... +} +{ + Ruby false positive rb_extend_object + Memcheck:Leak + ... + fun:rb_extend_object + ... +} +{ + Ruby false positive rb_funcall + Memcheck:Leak + ... + fun:rb_funcall + ... +} +{ + Ruby false positive rb_intern + Memcheck:Leak + ... + fun:rb_intern + ... +} +{ + Ruby false positive rb_ivar_set + Memcheck:Leak + ... + fun:rb_ivar_set + ... +} +{ + Ruby false positive rb_module_new + Memcheck:Leak + ... + fun:rb_module_new + ... +} +{ + Ruby false positive rb_raise + Memcheck:Leak + ... + fun:rb_raise + ... +} +{ + Ruby false positive rb_rescue + Memcheck:Leak + ... + fun:rb_rescue + ... +} +{ + Ruby false positive rb_respond_to + Memcheck:Leak + ... + fun:rb_respond_to + ... +} +{ + Ruby false positive rb_thread_create: Threads are relased to a cache, so they may be reported as a leak + Memcheck:Leak + ... + fun:rb_thread_create + ... +} +{ + Ruby false positive rb_yield + Memcheck:Leak + ... + fun:rb_yield + ... +} +{ + Ruby false positive rb_iseq_new_with_opt + Memcheck:Leak + ... + fun:rb_iseq_new_with_opt + ... +} +{ + Ruby false positive rb_str_subseq + Memcheck:Leak + ... + fun:rb_str_subseq + ... +} +{ + Ruby false positive rb_iseq_constant_body_alloc + Memcheck:Leak + ... + fun:rb_iseq_constant_body_alloc + ... +} +{ + Ruby false positive rb_singleton_class + Memcheck:Leak + ... + fun:rb_singleton_class + ... +} +{ + Ruby false positive rb_include_module + Memcheck:Leak + ... + fun:rb_include_module + ... +} +{ + Ruby false positive rb_define_class + Memcheck:Leak + ... + fun:rb_define_class + ... +} +{ + Ruby false positive rb_enc_str_new + Memcheck:Leak + ... + fun:rb_enc_str_new + ... +} +{ + On platforms where memcpy is safe for overlapped memory, the compiler will sometimes replace memmove with memcpy. Valgrind may report a false positive. + Memcheck:Overlap + fun:__memcpy_chk + fun:memmove + ... +} +{ + Requiring a file will add it to the loaded features, which may be reported as a leak. + Memcheck:Leak + ... + fun:require_internal + ... +} +{ + Remove this after Ruby 2.7.7, 3.0.5, 3.1.3 are relased. See: https://github.com/Shopify/ruby_memcheck/issues/6 + Memcheck:Leak + ... + fun:stack_chunk_alloc + ... +} +{ + recursive_list_access creates a hash called `list` that is stored on the threadptr_recursive_hash. This is reported as a memory leak. + Memcheck:Leak + ... + fun:rb_ident_hash_new + fun:recursive_list_access + fun:exec_recursive + ... +} +{ + "Invalid read of size 8" when marking the stack of fibers + Memcheck:Addr8 + fun:each_location* + ... +} +{ + Rust probes for statx(buf), will be fixed in Valgrind >= 3.1.6.0 + Memcheck:Param + statx(buf) + ... + fun:*try_statx* + ... +} +{ + Rust probes for statx(file_name), will be fixed in Valgrind >= 3.1.6.0 + Memcheck:Param + statx(file_name) + ... + fun:*try_statx* + ... +} diff --git a/source/tests/memcheck/valgrind-suppressions.sh b/source/tests/memcheck/valgrind-suppressions.sh new file mode 100644 index 0000000000..692092060a --- /dev/null +++ b/source/tests/memcheck/valgrind-suppressions.sh @@ -0,0 +1,56 @@ +#! /usr/bin/awk -f +# A script to extract the actual suppression info from the output of (for example) valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all ./minimal +# The desired bits are between ^{ and ^} (including the braces themselves). +# The combined output should either be appended to /usr/lib/valgrind/default.supp, or placed in a .supp of its own +# If the latter, either tell valgrind about it each time with --suppressions=, or add that line to ~/.valgrindrc + +# NB This script uses the |& operator, which I believe is gawk-specific. In case of failure, check that you're using gawk rather than some other awk + +# The script looks for suppressions. When it finds one it stores it temporarily in an array, +# and also feeds it line by line to the external app 'md5sum' which generates a unique checksum for it. +# The checksum is used as an index in a different array. If an item with that index already exists the suppression must be a duplicate and is discarded. + +BEGIN { suppression=0; md5sum = "md5sum" } + # If the line begins with '{', it's the start of a supression; so set the var and initialise things + /^{/ { + suppression=1; i=0; next + } + # If the line begins with '}' its the end of a suppression + /^}/ { + if (suppression) + { suppression=0; + close(md5sum, "to") # We've finished sending data to md5sum, so close that part of the pipe + ProcessInput() # Do the slightly-complicated stuff in functions + delete supparray # We don't want subsequent suppressions to append to it! + } + } + # Otherwise, it's a normal line. If we're inside a supression, store it, and pipe it to md5sum. Otherwise it's cruft, so ignore it + { if (suppression) + { + supparray[++i] = $0 + print |& md5sum + } + } + +function ProcessInput() +{ + # Pipe the result from md5sum, then close it + md5sum |& getline result + close(md5sum) + # gawk can't cope with enormous ints like $result would be, so stringify it first by prefixing a definite string + resultstring = "prefix"result + + if (! (resultstring in chksum_array) ) + { chksum_array[resultstring] = 0; # This checksum hasn't been seen before, so add it to the array + OutputSuppression() # and output the contents of the suppression + } +} + +function OutputSuppression() +{ + # A suppression is surrounded by '{' and '}'. Its data was stored line by line in the array + print "{" + for (n=1; n <= i; ++n) + { print supparray[n] } + print "}" +} diff --git a/source/tests/memcheck/valgrind-wasm.supp b/source/tests/memcheck/valgrind-wasm.supp new file mode 100644 index 0000000000..946936357f --- /dev/null +++ b/source/tests/memcheck/valgrind-wasm.supp @@ -0,0 +1,61 @@ +{ + Ignore memory leaked from wasm_engine_new. + Memcheck:Leak + ... + fun:wasm_engine_new + ... +} + +{ + Ignore memory leaked by wasm_instance_new. + Memcheck:Leak + ... + fun:*wasmtime*module*registry*ModuleRegistry*register* + ... + fun:wasm_instance_new + ... +} + +{ + Ignore memory leaked by wasm_store_delete. + Memcheck:Leak + ... + fun:wasm_store_delete + ... +} + +{ + Ignore memory leaked by Wasmtime trap handlers. + Memcheck:Leak + ... + fun:*wasmtime_runtime*traphandlers*trap_handler* + ... +} + +{ + Ignore memory leaked by dlopen when using dynlink_load. + Memcheck:Leak + ... + fun:dlopen + ... + fun:dynlink_load + ... +} + +{ + Ignore memory leaked by dlclose when using dynlink_unload. + Memcheck:Leak + ... + fun:dlclose + ... + fun:dynlink_unload + ... +} + +{ + Ignore memory leaked by dl_open_worker. + Memcheck:Leak + ... + fun:dl_open_worker + ... +} diff --git a/source/tests/memcheck/valgrind.supp b/source/tests/memcheck/valgrind.supp deleted file mode 100644 index e34e359fd4..0000000000 --- a/source/tests/memcheck/valgrind.supp +++ /dev/null @@ -1,7 +0,0 @@ -# GLIBC -{ - Ignore dlopen allocation memory leak from GLIB. - Memcheck:Leak - ... - fun:dlopen@@GLIBC_2.2.5 -} diff --git a/source/tests/metacall_backtrace_plugin_test/CMakeLists.txt b/source/tests/metacall_backtrace_plugin_test/CMakeLists.txt new file mode 100644 index 0000000000..900355faad --- /dev/null +++ b/source/tests/metacall_backtrace_plugin_test/CMakeLists.txt @@ -0,0 +1,166 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_PLUGINS_BACKTRACE) + return() +endif() + +include(Portability) + +if(NOT PROJECT_OS_FAMILY STREQUAL unix) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-backtrace-plugin-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_backtrace_plugin_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + BACKTRACE_PLUGIN_PATH="${PROJECT_OUTPUT_DIR}/plugins/backtrace_plugin/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + backtrace_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + # Disable valgrind when testing backtracing for segfaults + PROPERTY LABELS ${target} MEMCHECK_IGNORE +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_backtrace_plugin_test/source/main.cpp b/source/tests/metacall_backtrace_plugin_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_backtrace_plugin_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_backtrace_plugin_test/source/metacall_backtrace_plugin_test.cpp b/source/tests/metacall_backtrace_plugin_test/source/metacall_backtrace_plugin_test.cpp new file mode 100644 index 0000000000..ff2844f7ad --- /dev/null +++ b/source/tests/metacall_backtrace_plugin_test/source/metacall_backtrace_plugin_test.cpp @@ -0,0 +1,64 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#include + +void badass_function(void) +{ +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Warray-bounds" +#elif defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Warray-bounds" + #pragma GCC diagnostic ignored "-Wstringop-overflow=" +#endif + + char *ptr = (char *)42; + *ptr = 42; + +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) + #pragma GCC diagnostic pop +#endif +} + +class metacall_backtrace_plugin_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_backtrace_plugin_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Generate a segmentation fault in order to catch it by backtrace plugin */ + EXPECT_DEATH({ badass_function(); }, ""); + + metacall_destroy(); +} diff --git a/source/tests/metacall_c_lib_test/CMakeLists.txt b/source/tests/metacall_c_lib_test/CMakeLists.txt new file mode 100644 index 0000000000..d3a3750646 --- /dev/null +++ b/source/tests/metacall_c_lib_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_C) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-c-lib-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_c_lib_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + c_loader + loadtest +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_c_lib_test/source/main.cpp b/source/tests/metacall_c_lib_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_c_lib_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_c_lib_test/source/metacall_c_lib_test.cpp b/source/tests/metacall_c_lib_test/source/metacall_c_lib_test.cpp new file mode 100644 index 0000000000..1f61c2b179 --- /dev/null +++ b/source/tests/metacall_c_lib_test/source/metacall_c_lib_test.cpp @@ -0,0 +1,102 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_c_lib_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_c_lib_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_load_from_package("c", "loadtest", NULL)); + + void *ret = metacall("call_cpp_func"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_LONG); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)323); + + metacall_value_destroy(ret); + + void *pair_list_ptr = metacall_value_create_ptr(NULL); + + void *args_init[] = { + metacall_value_create_ptr(pair_list_ptr), + }; + + std::cout << "pair_list_ptr: " << pair_list_ptr << std::endl; + std::cout << "pair_list_ptr: *(" << metacall_value_to_ptr(pair_list_ptr) << ")" << std::endl; + + ret = metacallv("pair_list_init", args_init); + + std::cout << "pair_list_ptr: " << pair_list_ptr << std::endl; + std::cout << "pair_list_ptr: *(" << metacall_value_to_ptr(pair_list_ptr) << ")" << std::endl; + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_INT); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + + metacall_value_destroy(args_init[0]); + + void *args_value[] = { + pair_list_ptr, + metacall_value_create_int(2) + }; + + ret = metacallv("pair_list_value", args_value); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)2.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(args_value[1]); + + void *args_destroy[] = { + pair_list_ptr, + }; + + ret = metacallv("pair_list_destroy", args_destroy); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + metacall_value_destroy(pair_list_ptr); + + metacall_destroy(); +} diff --git a/source/tests/metacall_c_metacall_test/CMakeLists.txt b/source/tests/metacall_c_metacall_test/CMakeLists.txt new file mode 100644 index 0000000000..00d36ad944 --- /dev/null +++ b/source/tests/metacall_c_metacall_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_C) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-c-metacall-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_c_metacall_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + METACALL_INCLUDE_DIR="${CMAKE_SOURCE_DIR}/source/metacall/include" + METACALL_API_INCLUDE_DIR="${CMAKE_BINARY_DIR}/source/metacall/include" + METACALL_LIBRARY="${PROJECT_OUTPUT_DIR}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_c_metacall_test/source/main.cpp b/source/tests/metacall_c_metacall_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_c_metacall_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_c_metacall_test/source/metacall_c_metacall_test.cpp b/source/tests/metacall_c_metacall_test/source/metacall_c_metacall_test.cpp new file mode 100644 index 0000000000..f26e1471b3 --- /dev/null +++ b/source/tests/metacall_c_metacall_test/source/metacall_c_metacall_test.cpp @@ -0,0 +1,70 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_c_metacall_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_c_metacall_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, metacall_execution_path("c", METACALL_INCLUDE_DIR)); + ASSERT_EQ((int)0, metacall_execution_path("c", METACALL_API_INCLUDE_DIR)); + ASSERT_EQ((int)0, metacall_execution_path("c", METACALL_LIBRARY)); + + ASSERT_EQ((int)0, (int)metacall_load_from_package("c", "metacall", NULL)); + + void *ret = metacall("metacall_print_info"); + + ASSERT_EQ((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_STRING); + + EXPECT_EQ((int)0, strncmp(metacall_value_to_string(ret), "MetaCall", 8)); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_c_test/CMakeLists.txt b/source/tests/metacall_c_test/CMakeLists.txt new file mode 100644 index 0000000000..a5c3e0edde --- /dev/null +++ b/source/tests/metacall_c_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_C) + return() +endif() + +# +# External dependencies +# + +find_package(LibFFI) + +if(NOT LIBFFI_FOUND) + message(SEND_ERROR "Foreing Function Interface library not found") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-c-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_c_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + LIBFFI_INCLUDE_DIR="${LIBFFI_INCLUDE_DIR}" + LIBFFI_LIBRARY="${LIBFFI_LIBRARY}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_c_test/source/main.cpp b/source/tests/metacall_c_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_c_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_c_test/source/metacall_c_test.cpp b/source/tests/metacall_c_test/source/metacall_c_test.cpp new file mode 100644 index 0000000000..9b4bf3f6bc --- /dev/null +++ b/source/tests/metacall_c_test/source/metacall_c_test.cpp @@ -0,0 +1,435 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_c_test : public testing::Test +{ +protected: +}; + +void *sum_callback(size_t argc, void *args[], void *data) +{ + int a = metacall_value_to_int(args[0]); + int b = metacall_value_to_int(args[1]); + int result = a + b; + + (void)argc; + (void)data; + + printf("sum(%d, %d) == %d\n", a, b, result); + + return metacall_value_create_int(result); +} + +void *test_string_reference(size_t argc, void *args[], void *data) +{ + (void)argc; + (void)data; + + /* Get string from pointer */ + printf("ptr %p\n", args[0]); + fflush(stdout); + + void *string_value = metacall_value_to_ptr(args[0]); + + printf("string ptr %p\n", string_value); + printf("type id %s\n", metacall_value_type_name(string_value)); + fflush(stdout); + + char *str = metacall_value_to_string(string_value); + + printf("native string %s\n", str); + + /* Check it is a valid string */ + EXPECT_STREQ("asd", str); + + /* Replace the string, it will be choped by the previous length */ + static const char yeet[] = "yeet"; + + metacall_value_from_string(string_value, yeet, sizeof(yeet) - 1); + + printf("type id %s\n", metacall_value_type_name(string_value)); + printf("native string %s\n", str); + fflush(stdout); + + EXPECT_STREQ("yee", str); + + /* Define a new string in the pointer value */ + static const char hello[] = "hello world"; + + metacall_value_from_ptr(args[0], metacall_value_create_string(hello, sizeof(hello) - 1)); + + return metacall_value_create_null(); +} + +TEST_F(metacall_c_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* File */ + const char *c_scripts[] = { + "compiled.c" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + + void *ret = metacall("compiled_sum", 3, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); + + metacall_value_destroy(ret); + + /* https://github.com/metacall/core/issues/570 */ + { + /* void apply_blur_filter(int pixels[], int width, int height) */ + + /* Call by array */ + { + void *args[] = { + metacall_value_create_array(NULL, 100), + metacall_value_create_int(10), + metacall_value_create_int(10) + }; + + void **array_ptr = metacall_value_to_array(args[0]); + + for (int i = 0; i < 100; ++i) + { + array_ptr[i] = metacall_value_create_int(i); + } + + std::cout << "value: " << args[0] << std::endl; + std::cout << "array: " << array_ptr << std::endl; + + ret = metacallv("apply_blur_filter", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(args[2]); + } + + /* Call by pointer */ + { + int array[100]; + + void *args[] = { + metacall_value_create_ptr(array), + metacall_value_create_int(10), + metacall_value_create_int(10) + }; + + for (int i = 0; i < 100; ++i) + { + array[i] = i; + } + + ret = metacallv("apply_blur_filter", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(args[2]); + } + + /* double calculate_brightness(int pixels[], int size) */ + + /* Call by array */ + { + void *args[] = { + metacall_value_create_array(NULL, 100), + metacall_value_create_int(100) + }; + + void **array_ptr = metacall_value_to_array(args[0]); + + for (int i = 0; i < 100; ++i) + { + array_ptr[i] = metacall_value_create_int(i); + } + + std::cout << "value: " << args[0] << std::endl; + std::cout << "array: " << array_ptr << std::endl; + + ret = metacallv("calculate_brightness", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_DOUBLE); + + std::cout << "result: " << metacall_value_to_double(ret) << std::endl; + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)49.5); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Call by pointer */ + { + int array[100]; + + void *args[] = { + metacall_value_create_ptr(array), + metacall_value_create_int(100) + }; + + for (int i = 0; i < 100; ++i) + { + array[i] = i; + } + + ret = metacallv("calculate_brightness", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_DOUBLE); + + std::cout << "result: " << metacall_value_to_double(ret) << std::endl; + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + } + + /* File with dependencies */ + const char *c_dep_scripts[] = { + "ffi.c", + "ffi.ld" + }; + + /* Set dependency paths */ + EXPECT_EQ((int)0, (int)metacall_execution_path("c", LIBFFI_INCLUDE_DIR)); + EXPECT_EQ((int)0, (int)metacall_execution_path("c", LIBFFI_LIBRARY)); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_dep_scripts, sizeof(c_dep_scripts) / sizeof(c_dep_scripts[0]), NULL)); + + ret = metacall("call_fp_address"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_PTR); + + EXPECT_NE((void *)metacall_value_to_ptr(ret), (void *)NULL); + + metacall_value_destroy(ret); + + ret = metacall("int_type_renaming"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_INT); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)345); + + metacall_value_destroy(ret); + + /* Native register */ + metacall_register("sum_callback", sum_callback, NULL, METACALL_INT, 2, METACALL_INT, METACALL_INT); + + void *func = metacall_function("sum_callback"); + + EXPECT_NE((void *)NULL, (void *)func); + + void *args[] = { + metacall_value_create_function(func) + }; + + ret = metacallv_s("c_callback", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_INT); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)7); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + + /* Memory */ + { + const char c_buffer[] = { + "int compiled_mult_memory(int a, int b) { return a * b; }" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("c", c_buffer, sizeof(c_buffer), NULL)); + + void *ret = metacall("compiled_mult_memory", 3, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)12); + + metacall_value_destroy(ret); + } + + /* References (Native) */ + { + static const char str[] = "asd"; + void *str_value = metacall_value_create_string(str, sizeof(str) - 1); + void *str_value_ref = metacall_value_reference(str_value); + + printf("ptr %p\n", str_value_ref); + printf("string %p\n", str_value); + printf("string str %s\n", metacall_value_to_string(str_value)); + fflush(stdout); + + { + void *new_str_value = metacall_value_to_ptr(str_value_ref); + char *new_str = metacall_value_to_string(new_str_value); + + EXPECT_STREQ("asd", new_str); + } + + void *args[] = { + str_value_ref + }; + + metacall_register("test_string_reference", test_string_reference, NULL, METACALL_NULL, 1, METACALL_PTR); + + ret = metacallv_s("test_string_reference", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + printf("type id %s\n", metacall_value_type_name(str_value)); + fflush(stdout); + + /* It chops the string because it has a fixed size from 'asd' */ + EXPECT_STREQ(metacall_value_to_string(str_value), "yee"); + + metacall_value_destroy(str_value); + + /* It should contain the new string */ + void *new_str = metacall_value_dereference(str_value_ref); + + EXPECT_STREQ(metacall_value_to_string(new_str), "hello world"); + + metacall_value_destroy(new_str); + metacall_value_destroy(str_value_ref); + } + + /* References (C: string) */ + { + static const char str[] = "asd"; + void *str_value = metacall_value_create_string(str, sizeof(str) - 1); + void *str_value_ref = metacall_value_reference(str_value); + void *str_value_ref_ref = metacall_value_reference(str_value_ref); + + printf("(R) ptr %p\n", str_value_ref); + printf("(R) string ptr %p\n", str_value); + printf("(R) string str %s\n", metacall_value_to_string(str_value)); + fflush(stdout); + + void *args[] = { + str_value_ref_ref + }; + + ret = metacallv_s("modify_str_ptr", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + char *str_value_deref = static_cast(metacall_value_dereference(str_value_ref)); + + EXPECT_STREQ(str_value_deref, "yeet"); + + metacall_value_destroy(str_value); + metacall_value_destroy(str_value_ref); + metacall_value_destroy(str_value_ref_ref); + } + + /* References (C: int) */ + { + void *int_value = metacall_value_create_long(324444L); + void *int_value_ref = metacall_value_reference(int_value); + + printf("(R) ptr %p\n", int_value_ref); + printf("(R) int ptr %p\n", int_value); + printf("(R) int value %ld\n", metacall_value_to_long(int_value)); + fflush(stdout); + + void *args[] = { + int_value_ref + }; + + ret = metacallv_s("modify_int_ptr", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_NULL); + + metacall_value_destroy(ret); + + EXPECT_EQ((long)metacall_value_to_long(int_value), (long)111L); + + metacall_value_destroy(int_value); + metacall_value_destroy(int_value_ref); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_callback_complex_test/CMakeLists.txt b/source/tests/metacall_callback_complex_test/CMakeLists.txt new file mode 100644 index 0000000000..ddf680698a --- /dev/null +++ b/source/tests/metacall_callback_complex_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-callback-complex-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_callback_complex_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_callback_complex_test/source/main.cpp b/source/tests/metacall_callback_complex_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_callback_complex_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_callback_complex_test/source/metacall_callback_complex_test.cpp b/source/tests/metacall_callback_complex_test/source/metacall_callback_complex_test.cpp new file mode 100644 index 0000000000..9906b0824e --- /dev/null +++ b/source/tests/metacall_callback_complex_test/source/metacall_callback_complex_test.cpp @@ -0,0 +1,155 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_callback_complex_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_callback_complex_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + /* This is the equivalent code of this test in NodeJS */ + /* + const { function_chain, py_function_factorial } = require('function.py'); + const py_py_factorial = function_chain(py_function_factorial); + assert.notStrictEqual(py_py_factorial, undefined); + assert.strictEqual(py_py_factorial(5), 120); + */ + + const char *py_scripts[] = { + "function.py" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + void *py_function_factorial = metacall_function("py_function_factorial"); + ASSERT_NE((void *)NULL, (void *)py_function_factorial); + + void *v_py_function_factorial = metacall_value_create_function(py_function_factorial); + ASSERT_NE((void *)NULL, (void *)v_py_function_factorial); + + void *args[] = { v_py_function_factorial }; + void *py_py_factorial = metacallv("function_chain", args); + ASSERT_NE((void *)NULL, (void *)py_py_factorial); + + void *args_fact[] = { metacall_value_create_long(5L) }; + void *ret = metacallfv(metacall_value_to_function(py_py_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((long)120L, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + + ret = metacallfv(metacall_value_to_function(py_py_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((long)120L, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + + metacall_value_destroy(args_fact[0]); + + metacall_value_destroy(v_py_function_factorial); + metacall_value_destroy(py_py_factorial); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* This is the equivalent code of this test in NodeJS */ + /* + const { js_function_chain, js_function_factorial } = require('factcallback.js'); + const js_js_factorial = js_function_chain(js_function_factorial); + assert.notStrictEqual(js_js_factorial, undefined); + assert.strictEqual(js_js_factorial(5), 120); + */ + + const char *node_scripts[] = { + "factcallback.js" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + void *js_function_factorial = metacall_function("js_function_factorial"); + ASSERT_NE((void *)NULL, (void *)js_function_factorial); + + void *v_js_function_factorial = metacall_value_create_function(js_function_factorial); + ASSERT_NE((void *)NULL, (void *)v_js_function_factorial); + EXPECT_EQ((enum metacall_value_id)METACALL_FUNCTION, (enum metacall_value_id)metacall_value_id(v_js_function_factorial)); + + void *args[] = { v_js_function_factorial }; + void *js_js_factorial = metacallv("js_function_chain", args); + ASSERT_NE((void *)NULL, (void *)js_js_factorial); + EXPECT_EQ((enum metacall_value_id)METACALL_FUNCTION, (enum metacall_value_id)metacall_value_id(js_js_factorial)); + + void *args_fact[] = { metacall_value_create_long(5L) }; + void *ret = metacallfv(metacall_value_to_function(js_js_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((double)120.0, (double)metacall_value_to_double(ret)); + metacall_value_destroy(ret); + + ret = metacallfv(metacall_value_to_function(js_js_factorial), args_fact); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + EXPECT_EQ((double)120.0, (double)metacall_value_to_double(ret)); + metacall_value_destroy(ret); + + metacall_value_destroy(args_fact[0]); + + metacall_value_destroy(v_js_function_factorial); + metacall_value_destroy(js_js_factorial); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_cast_test/CMakeLists.txt b/source/tests/metacall_cast_test/CMakeLists.txt index 12f584b863..674a145164 100644 --- a/source/tests/metacall_cast_test/CMakeLists.txt +++ b/source/tests/metacall_cast_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + # # Executable name and options # @@ -80,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_cast_test/source/main.cpp b/source/tests/metacall_cast_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_cast_test/source/main.cpp +++ b/source/tests/metacall_cast_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_cast_test/source/metacall_cast_test.cpp b/source/tests/metacall_cast_test/source/metacall_cast_test.cpp index e6bcc8c40f..9b73a35de2 100644 --- a/source/tests/metacall_cast_test/source/metacall_cast_test.cpp +++ b/source/tests/metacall_cast_test/source/metacall_cast_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,11 @@ * */ -#include +#include #include #include -#include -#include - -#include - class metacall_cast_test : public testing::Test { public: @@ -35,21 +30,18 @@ class metacall_cast_test : public testing::Test TEST_F(metacall_cast_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; @@ -57,70 +49,62 @@ TEST_F(metacall_cast_test, DefaultConstructor) int iterator; - value ret = NULL; + void *ret = NULL; - value args[2]; + void *args[2]; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - args[0] = value_create_int(5); - args[1] = value_create_int(15); + args[0] = metacall_value_create_long(5L); + args[1] = metacall_value_create_long(15L); ret = metacallv("multiply", args); - EXPECT_EQ((int) 75, (int) value_to_int(ret)); - - value_destroy(ret); + EXPECT_EQ((int)75L, (int)metacall_value_to_long(ret)); - log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); + metacall_value_destroy(ret); - args[0] = value_from_int(args[0], 7); + args[0] = metacall_value_from_int(args[0], 7); for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) { - args[1] = value_from_int(args[1], iterator); + args[1] = metacall_value_from_int(args[1], iterator); ret = metacallv("multiply", args); - EXPECT_NE((value) NULL, (value) ret); - - ret = value_type_cast(ret, TYPE_INT); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) (7 * iterator), (int) value_to_int(ret)); + EXPECT_EQ((int)(7 * iterator), (int)metacall_value_cast_int(&ret)); - value_destroy(ret); + metacall_value_destroy(ret); } - args[0] = value_from_float(value_type_cast(args[0], TYPE_FLOAT), 64.0f); - args[1] = value_from_float(value_type_cast(args[1], TYPE_FLOAT), 2.0f); + args[0] = metacall_value_from_float(metacall_value_cast(args[0], METACALL_FLOAT), 64.0f); + args[1] = metacall_value_from_float(metacall_value_cast(args[1], METACALL_FLOAT), 2.0f); ret = metacallv("divide", args); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - ret = value_type_cast(ret, TYPE_FLOAT); + EXPECT_EQ((float)32.0f, (float)metacall_value_cast_float(&ret)); - EXPECT_EQ((float) 32.0f, (float) value_to_float(ret)); + metacall_value_destroy(ret); - value_destroy(ret); - - args[0] = value_from_int(value_type_cast(args[0], TYPE_INT), 1000); - args[1] = value_from_int(value_type_cast(args[1], TYPE_INT), 3500); + args[0] = metacall_value_from_int(metacall_value_cast(args[0], METACALL_INT), 1000); + args[1] = metacall_value_from_int(metacall_value_cast(args[1], METACALL_INT), 3500); ret = metacallv("sum", args); - EXPECT_NE((value) NULL, (value) ret); - - ret = value_type_cast(ret, TYPE_INT); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 4500, (int) value_to_int(ret)); + EXPECT_EQ((int)4500, (int)metacall_value_cast_int(&ret)); - value_destroy(ret); + metacall_value_destroy(ret); - value_destroy(args[0]); - value_destroy(args[1]); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_clear_test/CMakeLists.txt b/source/tests/metacall_clear_test/CMakeLists.txt index 75264a6d49..6698be908a 100644 --- a/source/tests/metacall_clear_test/CMakeLists.txt +++ b/source/tests/metacall_clear_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_NODE) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,6 +140,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + # # Define test properties # diff --git a/source/tests/metacall_clear_test/source/main.cpp b/source/tests/metacall_clear_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_clear_test/source/main.cpp +++ b/source/tests/metacall_clear_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_clear_test/source/metacall_clear_test.cpp b/source/tests/metacall_clear_test/source/metacall_clear_test.cpp index 5e9be9ab8b..3eab668359 100644 --- a/source/tests/metacall_clear_test/source/metacall_clear_test.cpp +++ b/source/tests/metacall_clear_test/source/metacall_clear_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,13 +32,13 @@ TEST_F(metacall_clear_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = - "#!/usr/bin/python3.5\n" + "#!/usr/bin/env python3\n" "def multmem(left: int, right: int) -> int:\n" "\tresult = left * right;\n" "\tprint(left, ' * ', right, ' = ', result);\n" @@ -46,23 +46,81 @@ TEST_F(metacall_clear_test, DefaultConstructor) static const char tag[] = "py"; - void * handle = NULL; + void *handle = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), &handle)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), &handle)); - void * ret = metacall("multmem", 5, 15); + void *args[2] = { + metacall_value_create_long(5), + metacall_value_create_long(15) + }; - EXPECT_NE((void *) NULL, (void *) ret); + void *ret = metacallhv(handle, "multmem", args); - EXPECT_EQ((long) 75, (long) metacall_value_to_long(ret)); + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)75, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + + EXPECT_EQ((void *)NULL, (void *)metacall_function("multmem")); + + EXPECT_NE((void *)NULL, (void *)metacall_handle_function(handle, "multmem")); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + + EXPECT_EQ((void *)NULL, (void *)metacall_function("multmem")); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char script1[] = "function greet() { return 1 }\nmodule.exports = { greet }"; + static const char script2[] = "function greet() { return 2 }\nmodule.exports = { greet }"; + static const char script3[] = "function yeet() { return 3 }\nmodule.exports = { yeet }"; + + void *handle1 = NULL; + void *handle2 = NULL; + + void *ret; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", script1, sizeof(script1), &handle1)); + + ret = metacallhv(handle1, "greet", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)1.0, (double)metacall_value_to_double(ret)); + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", script2, sizeof(script2), &handle2)); + + ret = metacallhv(handle2, "greet", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)2.0, (double)metacall_value_to_double(ret)); metacall_value_destroy(ret); - EXPECT_EQ((int) 0, (int) metacall_clear(handle)); + // Now load script number 3 into handle number 2 + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", script3, sizeof(script3), &handle2)); + + ret = metacallhv(handle2, "yeet", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)3.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall_function("multmem")); + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + EXPECT_EQ((int)0, (int)metacall_clear(handle2)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_cli_core_plugin_await_test/CMakeLists.txt b/source/tests/metacall_cli_core_plugin_await_test/CMakeLists.txt new file mode 100644 index 0000000000..13284fb395 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/CMakeLists.txt @@ -0,0 +1,176 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cli-core-plugin-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cli_core_plugin_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +set(METACALL_PLUGIN_PATH "${PROJECT_OUTPUT_DIR}/metacall_cli_core_plugin_await_test") + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Plugin path + METACALL_PLUGIN_PATH="${METACALL_PLUGIN_PATH}" +) + +# Isolate the plugin into a different directory in order to make it fully reproducible +add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}/cli_core_plugin" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl/cli_core_plugin" "${METACALL_PLUGIN_PATH}/cli_core_plugin" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/plugins/await_test" "${METACALL_PLUGIN_PATH}/await_test" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + plugin_extension + node_port + cli_core_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # NodeJS Port path + "METACALL_NODE_PORT_PATH=${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) diff --git a/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/await_test.js b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/await_test.js new file mode 100644 index 0000000000..9332bc90d4 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/await_test.js @@ -0,0 +1,14 @@ +const { metacall_load_from_file } = require(process.env['METACALL_NODE_PORT_PATH']); + +function await__test(await_cb) { + metacall_load_from_file("node", ["nod.js"]); + hello_await = 'hello_boy_await(1,2)'; + console.log(await_cb); + + // TODO: This call generates a deadlock + // await_cb(hello_await); + + return 22; +} + +module.exports = await__test; diff --git a/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/metacall.json b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/metacall.json new file mode 100644 index 0000000000..c39f3d7e6b --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/plugins/await_test/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "await_test.js" + ] +} diff --git a/source/tests/metacall_cli_core_plugin_await_test/source/main.cpp b/source/tests/metacall_cli_core_plugin_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_cli_core_plugin_await_test/source/metacall_cli_core_plugin_await_test.cpp b/source/tests/metacall_cli_core_plugin_await_test/source/metacall_cli_core_plugin_await_test.cpp new file mode 100644 index 0000000000..04461e788e --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_await_test/source/metacall_cli_core_plugin_await_test.cpp @@ -0,0 +1,127 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_cli_core_plugin_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_cli_core_plugin_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *plugin_extension_handle = metacall_plugin_extension(); + void *cli_plugin_handle = NULL; + + ASSERT_NE((void *)NULL, (void *)plugin_extension_handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&cli_plugin_handle) + }; + + void *result = metacallhv_s(plugin_extension_handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + + /* Test eval */ + void *func = metacall_handle_function(cli_plugin_handle, "eval"); + + ASSERT_NE((void *)func, (void *)NULL); + + static const char eval_loader_str[] = "node"; + static const char eval_str[] = "console.log('hello world')"; + + void *args_eval[] = { + metacall_value_create_string(eval_loader_str, sizeof(eval_loader_str) - 1), + metacall_value_create_string(eval_str, sizeof(eval_str) - 1) + }; + + result = metacallfv_s(func, args_eval, sizeof(args_eval) / sizeof(args_eval[0])); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args_eval[0]); + metacall_value_destroy(args_eval[1]); + metacall_value_destroy(result); + + /* Test await */ + func = metacall_handle_function(cli_plugin_handle, "await"); + + ASSERT_NE((void *)func, (void *)NULL); + + void *args_test[] = { + metacall_value_create_function(func) + }; + + result = metacallhv_s(cli_plugin_handle, "await__test", args_test, sizeof(args_test) / sizeof(args_test[0])); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((double)22.0, (double)metacall_value_to_double(result)); + + metacall_value_destroy(args_test[0]); + metacall_value_destroy(result); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_cli_core_plugin_test/CMakeLists.txt b/source/tests/metacall_cli_core_plugin_test/CMakeLists.txt new file mode 100644 index 0000000000..f0ecb936bc --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_EXT OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cli-core-plugin-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cli_core_plugin_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + CLI_CORE_PLUGIN_PATH="${PROJECT_OUTPUT_DIR}/plugins/cli/repl/cli_core_plugin/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + py_loader + cli_core_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_cli_core_plugin_test/source/main.cpp b/source/tests/metacall_cli_core_plugin_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp b/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp new file mode 100644 index 0000000000..7b116647c6 --- /dev/null +++ b/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp @@ -0,0 +1,390 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +class metacall_cli_core_plugin_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_cli_core_plugin_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + void *handle = NULL; + EXPECT_EQ((int)0, (int)metacall_load_from_configuration(CLI_CORE_PLUGIN_PATH, &handle, allocator)); + + /* Test load */ + { + void *args[2]; + + args[0] = metacall_value_create_string("py", 2); + + char test_script[] = "example.py"; + void *test_script_v = metacall_value_create_string(test_script, strlen(test_script)); + args[1] = metacall_value_create_array((const void **)&test_script_v, 1); + + void *ret = metacallhv_s(handle, "load", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test load */ + { + void *args[2]; + args[0] = metacall_value_create_string("node", 4); + + char test_script[] = "nod.js"; + void *test_script_v = metacall_value_create_string(test_script, strlen(test_script)); + args[1] = metacall_value_create_array((const void **)&test_script_v, 1); + + void *ret = metacallhv_s(handle, "load", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test eval */ + { + void *args[2]; + + args[0] = metacall_value_create_string("py", 2); + + char func_call[] = "print('Testing core_plugin...')\n"; + args[1] = (void **)metacall_value_create_string(func_call, strlen(func_call)); + + void *ret = metacallhv_s(handle, "eval", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test call */ + { + char func_call[] = "multiply(7, 3)"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)21); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test call without args */ + { + char func_call[] = "hello()"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without last parenthesis */ + { + char func_call[] = "multiply(7, 3"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without last parenthesis but another character instead */ + { + char func_call[] = "multiply(7, 3-"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call super tricky multiply("()", "eee"- */ + { + char func_call[] = "multiply(\"()\", \"eee\"~"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call with only first parenthesis */ + { + char func_call[] = "multiply("; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without any parenthesis */ + { + char func_call[] = "multiply"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without name */ + { + char func_call[] = "()"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without name and with only one parenthesis */ + { + char func_call[] = "("; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without name and with only one parenthesis (the oposite) */ + { + char func_call[] = ")"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call completely empty */ + { + char func_call[] = ""; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "call", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test await */ + { + char func_call[] = "hello_boy_await(2, 2)"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_double(ret), (double)4); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test await without args */ + { + char func_call[] = "return_await()"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_STREQ((const char *)metacall_value_to_string(ret), (const char *)"Hello World"); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without last parenthesis */ + { + char func_call[] = "hello_boy_await(2, 2"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call with only first parenthesis */ + { + char func_call[] = "hello_boy_await("; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test malformed call without any parenthesis */ + { + char func_call[] = "hello_boy_await"; + void *args[] = { metacall_value_create_string(func_call, strlen(func_call)) }; + + void *ret = metacallhv_s(handle, "await", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + /* Test clear */ + { + void *args[2]; + + args[0] = metacall_value_create_string("py", 2); + + char test_script[] = "example.py"; + args[1] = metacall_value_create_string(test_script, strlen(test_script)); + + void *ret = metacallhv_s(handle, "clear", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } + + /* Test inspect */ + { + void *ret = metacallhv_s(handle, "inspect", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((long)metacall_value_to_int(ret), (long)0); + + metacall_value_destroy(ret); + } + + /* Test debug */ + { + void *args[] = { metacall_value_create_array(NULL, 3) }; + void **array = metacall_value_to_array(args[0]); + + array[0] = metacall_value_create_string("abc", sizeof("abc") - 1); + array[1] = metacall_value_create_string("cbd", sizeof("cbd") - 1); + array[2] = metacall_value_create_string("thc", sizeof("thc") - 1); + + void *ret = metacallhv_s(handle, "debug", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_STREQ((const char *)"{\n\t[0]: abc\n\t[1]: cbd\n\t[2]: thc\n}", (const char *)metacall_value_to_string(ret)); + + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_cobol_test/CMakeLists.txt b/source/tests/metacall_cobol_test/CMakeLists.txt new file mode 100644 index 0000000000..3667141bca --- /dev/null +++ b/source/tests/metacall_cobol_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_COB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_COB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cobol-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cobol_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + cob_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_cobol_test/source/main.cpp b/source/tests/metacall_cobol_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_cobol_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp b/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp new file mode 100644 index 0000000000..5f74c003ce --- /dev/null +++ b/source/tests/metacall_cobol_test/source/metacall_cobol_test.cpp @@ -0,0 +1,95 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_cobol_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_cobol_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Cobol */ +#if defined(OPTION_BUILD_LOADERS_COB) + { + const char *cob_scripts[] = { + "say.cob" + }; + + const enum metacall_value_id hello_string_ids[] = { + METACALL_STRING, METACALL_STRING + }; + + static const char tag[] = "cob"; + + void *ret = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, cob_scripts, sizeof(cob_scripts) / sizeof(cob_scripts[0]), NULL)); + + ret = metacallt_s("say", hello_string_ids, 2, "hello", "world"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + + /* This is a Python script on purpose, in order to test Cobol when it fails */ + static const char buffer[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_COB */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_configuration_default_test/CMakeLists.txt b/source/tests/metacall_configuration_default_test/CMakeLists.txt new file mode 100644 index 0000000000..8cf0d40070 --- /dev/null +++ b/source/tests/metacall_configuration_default_test/CMakeLists.txt @@ -0,0 +1,168 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-configuration-default-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_configuration_default_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +# Create a custom directory different from default for forcing metacall not to find the configuration +set(CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/configuration-default-test") + +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}) + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + + # Define custom build output directory + LIBRARY_OUTPUT_DIRECTORY "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + + RUNTIME_OUTPUT_DIRECTORY "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + + ARCHIVE_OUTPUT_DIRECTORY "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" + ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +if(WIN32) + # Windows requires metacall to be in the same folder as the executable in order to run the test properly + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CONFIGURATION_DEFAULT_OUTPUT_DIRECTORY} + ) +endif() + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/metacall_configuration_default_test/source/main.cpp b/source/tests/metacall_configuration_default_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_configuration_default_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_configuration_default_test/source/metacall_configuration_default_test.cpp b/source/tests/metacall_configuration_default_test/source/metacall_configuration_default_test.cpp new file mode 100644 index 0000000000..5cee9a0e7d --- /dev/null +++ b/source/tests/metacall_configuration_default_test/source/metacall_configuration_default_test.cpp @@ -0,0 +1,37 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_configuration_default_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_configuration_default_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + metacall_destroy(); +} diff --git a/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt b/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt index da8ff0eb62..04c9d805e4 100644 --- a/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt +++ b/source/tests/metacall_configuration_exec_path_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS OR NOT OPTION_BUILD_LOADERS_PY) +# Check if python loader is enabled +if(NOT OPTION_BUILD_LOADERS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -97,8 +92,8 @@ target_link_libraries(${target} ${DEFAULT_LIBRARIES} GTest - ${CMAKE_DL_LIBS} - ${META_PROJECT_NAME}::metacall_distributable + + ${META_PROJECT_NAME}::metacall ) # @@ -119,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Set test variables # @@ -152,6 +164,34 @@ set_property(TEST ${target} PROPERTY LABELS ${target} ) +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # ERROR: LeakSanitizer: detected memory leaks + # + # Direct leak of 551991 byte(s) in 221 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # Direct leak of 1344 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e388d5 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85 + # #1 0x7f38162370d4 in _PyObject_GC_Resize (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x2040d4) + # + # Direct leak of 64 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f381622e105 in PyThread_allocate_lock (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1fb105) + # + # Indirect leak of 238277 byte(s) in 249 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # SUMMARY: AddressSanitizer: 791676 byte(s) leaked in 474 allocation(s). + # + # For solving this, we should enable Python support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} @@ -161,8 +201,52 @@ test_environment_variables(${target} "CONFIGURATION_PATH=${PY_CONFIGURATION_PATH}/global.json" "SERIAL_LIBRARY_PATH=${SERIAL_LIBRARY_PATH}" "DETOUR_LIBRARY_PATH=${DETOUR_LIBRARY_PATH}" + "${TESTS_SANITIZER_ENVIRONMENT_VARIABLES}" ) +# +# External dependencies +# + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(Python3_FIND_ABI "ON" "ANY" "ANY") + find_package(Python3 COMPONENTS Development) + + # Fallback to release if not found + if(NOT Python3_Development_FOUND) + set(Python3_FIND_ABI) + find_package(Python3 COMPONENTS Development REQUIRED) + endif() +else() + find_package(Python3 COMPONENTS Development REQUIRED) +endif() + +# Find Python DLL +include(Portability) + +if(PROJECT_OS_FAMILY STREQUAL win32 AND Python3_LIBRARIES AND Python3_ROOT_DIR AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + foreach(library ${Python3_LIBRARIES}) + if(${library} MATCHES "[^_d][.]lib$") + # Get the library path with dll suffix + string(REGEX REPLACE "[.]lib$" ".dll" LIB_PATH ${library}) + # Get the library name + get_filename_component(LIB_NAME "${LIB_PATH}" NAME) + # Find the library in the Python3 root path + find_file(Python3_LIBRARY_NAME_PATH ${LIB_NAME} + PATHS ${Python3_ROOT_DIR} + NO_DEFAULT_PATH + ) + if(Python3_LIBRARY_NAME_PATH) + break() + endif() + endif() + endforeach() +endif() + +if(NOT Python3_LIBRARY_NAME_PATH) + set(Python3_LIBRARY_NAME_PATH "${Python3_LIBRARIES}") +endif() + # # Configure test data # diff --git a/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in b/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in index 4978ee0b2b..02a8e81f9c 100644 --- a/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in +++ b/source/tests/metacall_configuration_exec_path_test/data/configurations/py_loader.json.in @@ -1,5 +1,8 @@ { "execution_paths": [ "@PY_EXECUTION_PATH@" - ] + ], + "dependencies": { + "python": ["@Python3_LIBRARY_NAME_PATH@"] + } } diff --git a/source/tests/metacall_configuration_exec_path_test/data/scripts/main.py b/source/tests/metacall_configuration_exec_path_test/data/scripts/main.py index 20b2ffa9c9..82e72c896f 100644 --- a/source/tests/metacall_configuration_exec_path_test/data/scripts/main.py +++ b/source/tests/metacall_configuration_exec_path_test/data/scripts/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.5 +#!/usr/bin/env python3 import metacall_configuration_exec_path_test diff --git a/source/tests/metacall_configuration_exec_path_test/data/scripts/metacall_configuration_exec_path_test.py b/source/tests/metacall_configuration_exec_path_test/data/scripts/metacall_configuration_exec_path_test.py index 52d0c63b59..5a8d8d1560 100644 --- a/source/tests/metacall_configuration_exec_path_test/data/scripts/metacall_configuration_exec_path_test.py +++ b/source/tests/metacall_configuration_exec_path_test/data/scripts/metacall_configuration_exec_path_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.5 +#!/usr/bin/env python3 def hello_world(text): return 'Python hello_world: ' + text diff --git a/source/tests/metacall_configuration_exec_path_test/source/main.cpp b/source/tests/metacall_configuration_exec_path_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_configuration_exec_path_test/source/main.cpp +++ b/source/tests/metacall_configuration_exec_path_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp b/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp index 0b60d6b476..0219e58aaf 100644 --- a/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp +++ b/source/tests/metacall_configuration_exec_path_test/source/metacall_configuration_exec_path_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,43 +18,42 @@ * */ -#include +#include #include #include -class metacall_distributable_test : public testing::Test +class metacall_configuration_exec_path_test : public testing::Test { public: }; -TEST_F(metacall_distributable_test, DefaultConstructor) +TEST_F(metacall_configuration_exec_path_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "main.py" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); ret = metacall("main"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Python hello_world: test")); + EXPECT_STREQ(metacall_value_to_string(ret), "Python hello_world: test"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_configuration_exec_relative_path_test/CMakeLists.txt b/source/tests/metacall_configuration_exec_relative_path_test/CMakeLists.txt new file mode 100644 index 0000000000..93ad2822f8 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/CMakeLists.txt @@ -0,0 +1,265 @@ +# Check if python loader is enabled +if(NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-configuration-exec-relative-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_configuration_exec_relative_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Dependecies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Set test variables +# + +set(PY_LOADER_SCRIPT_PATH "${CMAKE_CURRENT_BINARY_DIR}/scripts") +set(PY_CONFIGURATION_PATH "${CMAKE_CURRENT_BINARY_DIR}/configurations") +set(PY_EXECUTION_PATH "${PY_LOADER_SCRIPT_PATH}/a/b/c/d/e") + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # ERROR: LeakSanitizer: detected memory leaks + # + # Direct leak of 551991 byte(s) in 221 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # Direct leak of 1344 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e388d5 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85 + # #1 0x7f38162370d4 in _PyObject_GC_Resize (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x2040d4) + # + # Direct leak of 64 byte(s) in 2 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f381622e105 in PyThread_allocate_lock (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1fb105) + # + # Indirect leak of 238277 byte(s) in 249 object(s) allocated from: + # #0 0x7f3819e399cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7f38161499c7 (/usr/lib/x86_64-linux-gnu/libpython3.9.so.1.0+0x1169c7) + # + # SUMMARY: AddressSanitizer: 791676 byte(s) leaked in 474 allocation(s). + # + # For solving this, we should enable Python support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + "LOADER_LIBRARY_PATH=${LOADER_LIBRARY_PATH}" + "LOADER_SCRIPT_PATH=${PY_LOADER_SCRIPT_PATH}" + "CONFIGURATION_PATH=${PY_CONFIGURATION_PATH}/global.json" + "SERIAL_LIBRARY_PATH=${SERIAL_LIBRARY_PATH}" + "DETOUR_LIBRARY_PATH=${DETOUR_LIBRARY_PATH}" + "${TESTS_SANITIZER_ENVIRONMENT_VARIABLES}" +) + +# +# External dependencies +# + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(Python3_FIND_ABI "ON" "ANY" "ANY") + find_package(Python3 COMPONENTS Development) + + # Fallback to release if not found + if(NOT Python3_Development_FOUND) + set(Python3_FIND_ABI) + find_package(Python3 COMPONENTS Development REQUIRED) + endif() +else() + find_package(Python3 COMPONENTS Development REQUIRED) +endif() + +# Find Python DLL +include(Portability) + +if(PROJECT_OS_FAMILY STREQUAL win32 AND Python3_LIBRARIES AND Python3_ROOT_DIR AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + foreach(library ${Python3_LIBRARIES}) + if(${library} MATCHES "[^_d][.]lib$") + # Get the library path with dll suffix + string(REGEX REPLACE "[.]lib$" ".dll" LIB_PATH ${library}) + # Get the library name + get_filename_component(LIB_NAME "${LIB_PATH}" NAME) + # Find the library in the Python3 root path + find_file(Python3_LIBRARY_NAME_PATH ${LIB_NAME} + PATHS ${Python3_ROOT_DIR} + NO_DEFAULT_PATH + ) + if(Python3_LIBRARY_NAME_PATH) + break() + endif() + endif() + endforeach() +endif() + +if(NOT Python3_LIBRARY_NAME_PATH) + set(Python3_LIBRARY_NAME_PATH "${Python3_LIBRARIES}") +endif() + +# +# Configure test data +# + +file(COPY data/scripts/main.py DESTINATION ${PY_LOADER_SCRIPT_PATH}) + +file(COPY data/scripts/metacall_configuration_exec_relative_path_test.py DESTINATION ${PY_EXECUTION_PATH}) + +# Set relative paths +set(PY_CONFIGURATION_OUTPUT_PATH "${PY_CONFIGURATION_PATH}") +set(PY_CONFIGURATION_PATH ".") +set(PY_EXECUTION_PATH "../scripts/a/b/c/d/e") + +configure_file(data/configurations/global.json.in ${PY_CONFIGURATION_OUTPUT_PATH}/global.json @ONLY) + +configure_file(data/configurations/py_loader.json.in ${PY_CONFIGURATION_OUTPUT_PATH}/py_loader.json @ONLY) diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/global.json.in b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/global.json.in new file mode 100644 index 0000000000..ca417e6a73 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/global.json.in @@ -0,0 +1,3 @@ +{ + "py_loader":"@PY_CONFIGURATION_PATH@/py_loader.json" +} diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/py_loader.json.in b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/py_loader.json.in new file mode 100644 index 0000000000..02a8e81f9c --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/configurations/py_loader.json.in @@ -0,0 +1,8 @@ +{ + "execution_paths": [ + "@PY_EXECUTION_PATH@" + ], + "dependencies": { + "python": ["@Python3_LIBRARY_NAME_PATH@"] + } +} diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/main.py b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/main.py new file mode 100644 index 0000000000..a34daba892 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/main.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import metacall_configuration_exec_relative_path_test + +def main(): + return metacall_configuration_exec_relative_path_test.hello_world('test') diff --git a/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/metacall_configuration_exec_relative_path_test.py b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/metacall_configuration_exec_relative_path_test.py new file mode 100644 index 0000000000..5a8d8d1560 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/data/scripts/metacall_configuration_exec_relative_path_test.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 + +def hello_world(text): + return 'Python hello_world: ' + text diff --git a/source/tests/metacall_configuration_exec_relative_path_test/source/main.cpp b/source/tests/metacall_configuration_exec_relative_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_configuration_exec_relative_path_test/source/metacall_configuration_exec_relative_path_test.cpp b/source/tests/metacall_configuration_exec_relative_path_test/source/metacall_configuration_exec_relative_path_test.cpp new file mode 100644 index 0000000000..cd56aa446e --- /dev/null +++ b/source/tests/metacall_configuration_exec_relative_path_test/source/metacall_configuration_exec_relative_path_test.cpp @@ -0,0 +1,59 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_configuration_exec_relative_path_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_configuration_exec_relative_path_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "main.py" + }; + + void *ret = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + ret = metacall("main"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "Python hello_world: test"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_cs_test/CMakeLists.txt b/source/tests/metacall_cs_test/CMakeLists.txt new file mode 100644 index 0000000000..6eb981ca3c --- /dev/null +++ b/source/tests/metacall_cs_test/CMakeLists.txt @@ -0,0 +1,209 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_CS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/environment.hpp +) + +set(sources + ${source_path}/main.cpp + ${source_path}/environment.cpp + ${source_path}/metacall_cs_test.cpp +) + +# Group source files +set(header_group "Header Files") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} + ${headers} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizer: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13983) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 environment::SetUp() /usr/local/metacall/source/tests/metacall_cs_test/source/environment.cpp:38 (metacall-cs-testd+0x21968) + # #18 testing::internal::UnitTestImpl::RunAllTests() (metacall-cs-testd+0x483c1) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x81e000278 pc=0x7f21968e07c8 sp=0x7ee064c83d20 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +# +# Define test properties +# + +set_property(TEST ${target} + # TODO: Valgrind also fails with C# + PROPERTY LABELS ${target} MEMCHECK_IGNORE +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/cs_loader_test/include/cs-loader-test/environment.hpp b/source/tests/metacall_cs_test/include/metacall-cs-test/environment.hpp similarity index 81% rename from source/tests/cs_loader_test/include/cs-loader-test/environment.hpp rename to source/tests/metacall_cs_test/include/metacall-cs-test/environment.hpp index 7aeff0c853..b2efb0121c 100644 --- a/source/tests/cs_loader_test/include/cs-loader-test/environment.hpp +++ b/source/tests/metacall_cs_test/include/metacall-cs-test/environment.hpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,9 @@ * */ -#include +#include -#include - -class environment : public testing::Environment +class environment : public testing::Environment { public: void SetUp(); diff --git a/source/tests/metacall_cs_test/source/environment.cpp b/source/tests/metacall_cs_test/source/environment.cpp new file mode 100644 index 0000000000..acd33b3c52 --- /dev/null +++ b/source/tests/metacall_cs_test/source/environment.cpp @@ -0,0 +1,44 @@ +/* + * MetaCall Library by Parra Studios + * A library for dynamic loading and linking shared objects at run-time. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include + +#include + +void environment::SetUp() +{ + const char *cs_scripts[] = { + "hello.cs", + "IJump.cs", + "JumpMaster.cs", + "SuperJump.cs", + "TinyJump.cs" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); +} + +void environment::TearDown() +{ + metacall_destroy(); +} diff --git a/source/tests/metacall_cs_test/source/main.cpp b/source/tests/metacall_cs_test/source/main.cpp new file mode 100644 index 0000000000..41f8024b41 --- /dev/null +++ b/source/tests/metacall_cs_test/source/main.cpp @@ -0,0 +1,29 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new environment()); + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_cs_test/source/metacall_cs_test.cpp b/source/tests/metacall_cs_test/source/metacall_cs_test.cpp new file mode 100644 index 0000000000..799b24ec00 --- /dev/null +++ b/source/tests/metacall_cs_test/source/metacall_cs_test.cpp @@ -0,0 +1,111 @@ +/* + * MetaCall Library by Parra Studios + * A library for dynamic loading and linking shared objects at run-time. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_cs_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_cs_test, SayHello) +{ + ASSERT_NE((void *)NULL, (void *)metacall_function("SayHello")); + + metacall("SayHello"); +} + +TEST_F(metacall_cs_test, SayAny) +{ + ASSERT_NE((void *)NULL, (void *)metacall_function("Say")); + + metacall("Say", "Any"); +} + +TEST_F(metacall_cs_test, Jump) +{ + void *ret = NULL; + + ASSERT_NE((void *)NULL, (void *)metacall_function("SuperJump")); + + ret = metacall("SuperJump"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)2, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); +} + +TEST_F(metacall_cs_test, Sum) +{ + void *ret = NULL; + + ASSERT_NE((void *)NULL, (void *)metacall_function("Sum")); + + ret = metacall("Sum", 5, 10); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); +} + +TEST_F(metacall_cs_test, Concat) +{ + void *ret = NULL; + + ASSERT_NE((void *)NULL, (void *)metacall_function("Concat")); + + ret = metacall("Concat", "Hello ", "World"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ((const char *)metacall_value_to_string(ret), "Hello World"); + + metacall_value_destroy(ret); +} + +TEST_F(metacall_cs_test, Fail) +{ + /* This is a Python script on purpose, in order to test C# when it fails */ + static const char buffer[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("cs", buffer, sizeof(buffer), NULL)); +} + +TEST_F(metacall_cs_test, FailRelativePath) +{ + /* TODO: Review source/ports/node_port/test/index.js in the fail/require section for more information */ + /* This test is not reproducing the same bug (segmentation fault), we should investigate why */ + static const char *scripts[] = { + "./asd.invalid" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("cs", scripts, sizeof(scripts) / sizeof(scripts[0]), NULL)); +} diff --git a/source/tests/metacall_csharp_function_test/CMakeLists.txt b/source/tests/metacall_csharp_function_test/CMakeLists.txt new file mode 100644 index 0000000000..0a70cedb5e --- /dev/null +++ b/source/tests/metacall_csharp_function_test/CMakeLists.txt @@ -0,0 +1,171 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_CS) + return() +endif() + +# +# External dependencies +# + +find_package(DotNET) + +if(NOT DOTNET_FOUND) + message(SEND_ERROR "DotNET command not found") + return() +endif() + +if(DOTNET_VERSION VERSION_LESS "5.0") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-csharp-function-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_csharp_function_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_csharp_function_test/source/main.cpp b/source/tests/metacall_csharp_function_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_csharp_function_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_csharp_function_test/source/metacall_csharp_function_test.cpp b/source/tests/metacall_csharp_function_test/source/metacall_csharp_function_test.cpp new file mode 100644 index 0000000000..541ff5476b --- /dev/null +++ b/source/tests/metacall_csharp_function_test/source/metacall_csharp_function_test.cpp @@ -0,0 +1,79 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_csharp_function_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_csharp_function_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + const char *cs_scripts[] = { + "function.cs" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + + void *ret = metacall("TopLevelFunc"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)8, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_CS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_csharp_static_class_test/CMakeLists.txt b/source/tests/metacall_csharp_static_class_test/CMakeLists.txt new file mode 100644 index 0000000000..7c2289c3dd --- /dev/null +++ b/source/tests/metacall_csharp_static_class_test/CMakeLists.txt @@ -0,0 +1,216 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_CS) + return() +endif() + +# +# External dependencies +# + +find_package(DotNET) + +if(NOT DOTNET_FOUND) + message(SEND_ERROR "DotNET command not found") + return() +endif() + +if(DOTNET_VERSION VERSION_LESS "5.0") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-csharp-static-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_csharp_static_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizer: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13974) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 metacall_csharp_static_class_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp:45 (metacall-csharp-static-class-testd+0x20d33) + # #18 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-csharp-static-class-testd+0x55fd6) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # Tracer caught signal 11: addr=0x500000330 pc=0x7fb0d9aa10f0 sp=0x7fb02658bd10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_csharp_static_class_test/source/main.cpp b/source/tests/metacall_csharp_static_class_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_csharp_static_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp b/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp new file mode 100644 index 0000000000..bfaf4f744b --- /dev/null +++ b/source/tests/metacall_csharp_static_class_test/source/metacall_csharp_static_class_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_csharp_static_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_csharp_static_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) + { + const char *cs_scripts[] = { + "static.cs" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + + void *ret = metacall("Said", "Hello World"); + + EXPECT_EQ((void *)NULL, (void *)ret); // TODO: NULL_TYPE not implemented yet + + ret = metacall("Three"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)3, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_CS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_cxx_port_test/CMakeLists.txt b/source/tests/metacall_cxx_port_test/CMakeLists.txt new file mode 100644 index 0000000000..1a4b07292f --- /dev/null +++ b/source/tests/metacall_cxx_port_test/CMakeLists.txt @@ -0,0 +1,151 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_CXX) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-cxx-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_cxx_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::cxx_port +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + LIBFFI_INCLUDE_DIR="${LIBFFI_INCLUDE_DIR}" + LIBFFI_LIBRARY="${LIBFFI_LIBRARY}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_cxx_port_test/source/main.cpp b/source/tests/metacall_cxx_port_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_cxx_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_cxx_port_test/source/metacall_cxx_port_test.cpp b/source/tests/metacall_cxx_port_test/source/metacall_cxx_port_test.cpp new file mode 100644 index 0000000000..90e987e0af --- /dev/null +++ b/source/tests/metacall_cxx_port_test/source/metacall_cxx_port_test.cpp @@ -0,0 +1,196 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_cxx_port_test : public testing::Test +{ +protected: +}; + +void *cxx_map_test(size_t argc, void *args[], void *data) +{ + metacall::map m(args[0]); + + (void)argc; + (void)data; + + EXPECT_EQ((float)m["hello"], (float)3.0f); + EXPECT_EQ((float)m["world"], (float)4.0f); + + printf("hello => %f\n", m["hello"]); + printf("world => %f\n", m["world"]); + fflush(stdout); + + return metacall::metacall_value_create_null(); +} + +void *cxx_array_test(size_t argc, void *args[], void *data) +{ + metacall::array a(args[0]); + + (void)argc; + (void)data; + + EXPECT_EQ((float)a[0].as(), (int)3); + EXPECT_EQ((float)a[1].as(), (float)4.0f); + + EXPECT_EQ((float)a.get(0), (int)3); + EXPECT_EQ((float)a.get(1), (float)4.0f); + + printf("a[0] => %d\n", a[0].as()); + printf("a[1] => %f\n", a[1].as()); + fflush(stdout); + + return metacall::metacall_value_create_null(); +} + +void *cxx_map_array_test(size_t argc, void *args[], void *data) +{ + metacall::map m(args[0]); + + (void)argc; + (void)data; + + EXPECT_STREQ(m["includes"][0].as().c_str(), "/a/path"); + EXPECT_STREQ(m["includes"][1].as().c_str(), "/another/path"); + + EXPECT_STREQ(m["libraries"][0].as().c_str(), "/a/path"); + EXPECT_STREQ(m["libraries"][1].as().c_str(), "/another/path"); + + printf("m['includes'][0] => %s\n", m["includes"][0].as().c_str()); + printf("m['includes'][1] => %s\n", m["includes"][1].as().c_str()); + + printf("m['libraries'][0] => %s\n", m["libraries"][0].as().c_str()); + printf("m['libraries'][1] => %s\n", m["libraries"][1].as().c_str()); + + return metacall::metacall_value_create_null(); +} + +// TODO: +/* +void *cxx_recursive_map_test(size_t argc, void *args[], void *data) +{ + metacall::map> m(args[0]); + + (void)argc; + (void)data; + + EXPECT_EQ((float)m["hello"]["world"], (float)4.0f); + + printf("hello => %f\n", m["hello"]["world"]); + fflush(stdout); + + return metacall_value_create_null(); +} +*/ + +void *cxx_float_int_int_test(size_t argc, void *args[], void *data) +{ + metacall::value a0(args[0]); + metacall::value a1(args[1]); + + (void)argc; + (void)data; + + EXPECT_EQ(a0.to_value(), 7); + EXPECT_EQ(a1.to_value(), 8); + + return metacall::metacall_value_create_float(3.0f); +} + +TEST_F(metacall_cxx_port_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall::metacall_initialize()); + + { + metacall::map m = { + { "hello", 3.0f }, + { "world", 4.0f } + }; + + metacall::metacall_register("cxx_map_test", cxx_map_test, NULL, metacall::METACALL_NULL, 1, metacall::METACALL_MAP); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_map_test", m)); + } + + { + metacall::array a(3, 4.0f); + + metacall::metacall_register("cxx_array_test", cxx_array_test, NULL, metacall::METACALL_NULL, 1, metacall::METACALL_ARRAY); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_array_test", a)); + } + + { + metacall::map m = { + { "includes", metacall::array("/a/path", "/another/path") }, + { "libraries", metacall::array("/a/path", "/another/path") } + }; + + metacall::metacall_register("cxx_map_array_test", cxx_map_array_test, NULL, metacall::METACALL_NULL, 1, metacall::METACALL_MAP); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_map_array_test", m)); + } + + // TODO: + /* + { + metacall::map> m = { + { "hello", { "world", 4.0f } } + }; + + metacall::metacall_register("cxx_recursive_map_test", cxx_recursive_map_test, NULL, metacall::METACALL_NULL, 1, metacall::METACALL_MAP); + + EXPECT_EQ(nullptr, metacall::metacall("cxx_recursive_map_test", m)); + } + */ + + { + metacall::metacall_register("cxx_float_int_int_test", cxx_float_int_int_test, NULL, metacall::METACALL_FLOAT, 2, metacall::METACALL_INT, metacall::METACALL_INT); + + EXPECT_EQ(3.0f, metacall::metacall("cxx_float_int_int_test", 7, 8)); + } + + /* Print inspect information */ + { + size_t size = 0; + + metacall::metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(metacall::METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall::metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall::metacall_allocator_free(allocator, inspect_str); + + metacall::metacall_allocator_destroy(allocator); + } + + metacall::metacall_destroy(); +} diff --git a/source/tests/metacall_depends_test/CMakeLists.txt b/source/tests/metacall_depends_test/CMakeLists.txt index 09b9f944ad..46fe418c77 100644 --- a/source/tests/metacall_depends_test/CMakeLists.txt +++ b/source/tests/metacall_depends_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if python loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_depends_test/source/main.cpp b/source/tests/metacall_depends_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_depends_test/source/main.cpp +++ b/source/tests/metacall_depends_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_depends_test/source/metacall_depends_test.cpp b/source/tests/metacall_depends_test/source/metacall_depends_test.cpp index dea2b32616..4ec81006c5 100644 --- a/source/tests/metacall_depends_test/source/metacall_depends_test.cpp +++ b/source/tests/metacall_depends_test/source/metacall_depends_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,37 +32,36 @@ TEST_F(metacall_depends_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "rsasample.py", "sample/rsa_strings.py" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_NE((void *) NULL, (void *) metacall_function("main")); + EXPECT_NE((void *)NULL, (void *)metacall_function("main")); ret = metacall("main"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); metacall_value_destroy(ret); - EXPECT_NE((void *) NULL, (void *) metacall_function("encript_decript_strings")); + EXPECT_NE((void *)NULL, (void *)metacall_function("encript_decript_strings")); ret = metacall("encript_decript_strings"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -70,18 +69,18 @@ TEST_F(metacall_depends_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t)size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); metacall_allocator_free(allocator, inspect_str); metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_distributable_test/CMakeLists.txt b/source/tests/metacall_distributable_test/CMakeLists.txt index 60d437d539..07d5581616 100644 --- a/source/tests/metacall_distributable_test/CMakeLists.txt +++ b/source/tests/metacall_distributable_test/CMakeLists.txt @@ -1,12 +1,3 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - # # Executable name and options # @@ -63,9 +54,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +88,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -134,10 +131,48 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13717) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #9 metacall_distributable_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp:262 (metacall-distributable-testd+0x23e3d) + # #10 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-distributable-testd+0x58686) + # #11 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader + rb_loader + cs_loader + jsm_loader + js_loader + mock_loader + c_loader +) + # # Define test properties # @@ -146,6 +181,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x600000690 pc=0x7f3a7b6710f0 sp=0x7f3a75e32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_distributable_test/source/main.cpp b/source/tests/metacall_distributable_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_distributable_test/source/main.cpp +++ b/source/tests/metacall_distributable_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp b/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp index 5095bf225b..30b2033cb5 100644 --- a/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp +++ b/source/tests/metacall_distributable_test/source/metacall_distributable_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,13 +32,12 @@ TEST_F(metacall_distributable_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; @@ -46,15 +45,15 @@ TEST_F(metacall_distributable_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); ret = metacall("multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -62,212 +61,220 @@ TEST_F(metacall_distributable_test, DefaultConstructor) { ret = metacall("multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall("hello")); + ret = metacall("hello"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "hello.rb", "second.rb" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); ret = metacall("say_multiply", 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 35); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)35); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall("say_null")); + ret = metacall("say_null"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello meta-programmer!"); metacall_value_destroy(ret); ret = metacall("get_second", 5, 12); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 12); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)12); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript SpiderMonkey */ - #if defined(OPTION_BUILD_LOADERS_JSM) +/* JavaScript SpiderMonkey */ +#if defined(OPTION_BUILD_LOADERS_JSM) { - const char * jsm_scripts[] = - { + const char *jsm_scripts[] = { "spider.jsm" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("say_spider", 8, 4)); + EXPECT_EQ((void *)NULL, (void *)metacall("say_spider", 8, 4)); } - #endif /* OPTION_BUILD_LOADERS_JSM */ +#endif /* OPTION_BUILD_LOADERS_JSM */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { - const char * js_scripts[] = - { + const char *js_scripts[] = { "divide.js", "third.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); ret = metacall("say_divide", 32.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 8.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)8.0); metacall_value_destroy(ret); ret = metacall("some_text", "abc", "def"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "abcdef")); + EXPECT_STREQ(metacall_value_to_string(ret), "abcdef"); metacall_value_destroy(ret); ret = metacall("third_value", "abc", "def", "efg"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "efg")); + EXPECT_STREQ(metacall_value_to_string(ret), "efg"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); ret = metacall("my_empty_func"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 1234); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)1234); metacall_value_destroy(ret); ret = metacall("two_doubles", 3.14, 68.3); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.1416); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.1416); metacall_value_destroy(ret); ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((char) metacall_value_to_char(ret), (char) 'A'); + EXPECT_EQ((char)metacall_value_to_char(ret), (char)'A'); metacall_value_destroy(ret); ret = metacall("new_args", "goodbye"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello World")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello World"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - /* C# Netcore */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("Say", "Hello para with params!")); + EXPECT_EQ((void *)NULL, (void *)metacall("Say", "Hello para with params!")); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ - /* C */ - #if defined(OPTION_BUILD_LOADERS_C) +/* C */ +#if defined(OPTION_BUILD_LOADERS_C) { - const char * c_scripts[] = - { + const char *c_scripts[] = { "compiled.c" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_C */ +#endif /* OPTION_BUILD_LOADERS_C */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_ducktype_test/CMakeLists.txt b/source/tests/metacall_ducktype_test/CMakeLists.txt index 35ae5c411c..c8d507e6f0 100644 --- a/source/tests/metacall_ducktype_test/CMakeLists.txt +++ b/source/tests/metacall_ducktype_test/CMakeLists.txt @@ -80,19 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +127,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + js_loader +) + # # Define test properties # diff --git a/source/tests/metacall_ducktype_test/source/main.cpp b/source/tests/metacall_ducktype_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_ducktype_test/source/main.cpp +++ b/source/tests/metacall_ducktype_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp b/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp index 4a0ade0777..0b8e2e2e84 100644 --- a/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp +++ b/source/tests/metacall_ducktype_test/source/metacall_ducktype_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_ducktype_test : public testing::Test { @@ -33,13 +33,12 @@ TEST_F(metacall_ducktype_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "ducktype.py" }; @@ -47,20 +46,19 @@ TEST_F(metacall_ducktype_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - const enum metacall_value_id multiply_ids[] = - { + const enum metacall_value_id multiply_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("multiply", multiply_ids, 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 75); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)75); metacall_value_destroy(ret); @@ -68,321 +66,325 @@ TEST_F(metacall_ducktype_test, DefaultConstructor) { ret = metacallt("multiply", multiply_ids, 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)(7 * iterator)); metacall_value_destroy(ret); } - const enum metacall_value_id divide_ids[] = - { + const enum metacall_value_id divide_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("divide", divide_ids, 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)32.0); metacall_value_destroy(ret); - const enum metacall_value_id sum_int_ids[] = - { + const enum metacall_value_id sum_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("sum", sum_int_ids, 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 4500); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)4500); metacall_value_destroy(ret); ret = metacallt("sum", sum_int_ids, 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 7); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)7); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall("hello")); + ret = metacall("hello"); - const enum metacall_value_id strcat_str_ids[] = - { + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); + + const enum metacall_value_id strcat_str_ids[] = { METACALL_STRING, METACALL_STRING }; ret = metacallt("strcat", strcat_str_ids, "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "Hello Universe"); metacall_value_destroy(ret); - const enum metacall_value_id strcat_float_ids[] = - { + const enum metacall_value_id strcat_float_ids[] = { METACALL_FLOAT, METACALL_FLOAT }; ret = metacallt("strcat", strcat_float_ids, 10.0f, 20.0f); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((float) metacall_value_cast_float(&ret), (float) 30.0f); + EXPECT_EQ((float)metacall_value_cast_float(&ret), (float)30.0f); metacall_value_destroy(ret); ret = metacall("old_style", 1, 2); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 3); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)3); metacall_value_destroy(ret); - const enum metacall_value_id mixed_int_ids[] = - { + const enum metacall_value_id mixed_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("mixed_style", mixed_int_ids, 200, 100); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_cast_long(&ret), (long) 300); + EXPECT_EQ((long)metacall_value_cast_long(&ret), (long)300); metacall_value_destroy(ret); - const enum metacall_value_id mixed_noret_int_ids[] = - { + const enum metacall_value_id mixed_noret_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("mixed_style_noreturn", mixed_noret_int_ids, 2, 6); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 8); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)8); metacall_value_destroy(ret); - void * args[2]; + void *args[2]; args[0] = metacall_value_create_int(5); args[1] = metacall_value_create_int(15); ret = metacallv("multiply", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 75); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)75); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); args[0] = metacall_value_create_double(15.0); args[1] = metacall_value_create_double(5.0); ret = metacallv("divide", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_double(&ret), (int) 3.0); + EXPECT_EQ((int)metacall_value_cast_double(&ret), (int)3.0); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); - const char pepico_str[] = "Pepico"; - const char walas_str[] = "Walas"; + static const char pepico_str[] = "Pepico"; + static const char walas_str[] = "Walas"; - args[0] = metacall_value_create_string(pepico_str, sizeof(pepico_str)); - args[1] = metacall_value_create_string(walas_str, sizeof(walas_str)); + args[0] = metacall_value_create_string(pepico_str, sizeof(pepico_str) - 1); + args[1] = metacall_value_create_string(walas_str, sizeof(walas_str) - 1); ret = metacallv("strcat", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "PepicoWalas")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "PepicoWalas"); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "ducktype.rb" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - const enum metacall_value_id say_multiply_int_ids[] = - { + const enum metacall_value_id say_multiply_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("say_multiply", say_multiply_int_ids, 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 35); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)35); metacall_value_destroy(ret); - const enum metacall_value_id say_null_invalid_ids[] = - { + const enum metacall_value_id say_null_invalid_ids[] = { METACALL_INVALID }; - EXPECT_EQ((void *) NULL, (void *) metacallt("say_null", say_null_invalid_ids)); + ret = metacallt("say_null", say_null_invalid_ids); - const enum metacall_value_id say_hello_str_ids[] = - { + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + const enum metacall_value_id say_hello_str_ids[] = { METACALL_STRING }; ret = metacallt("say_hello", say_hello_str_ids, "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "Hello meta-programmer!"); metacall_value_destroy(ret); - const enum metacall_value_id mixed_int_ids[] = - { + const enum metacall_value_id mixed_int_ids[] = { METACALL_INT, METACALL_INT, METACALL_INT, METACALL_INT }; ret = metacallt("mixed", mixed_int_ids, 1, 2, 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 10); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)10); metacall_value_destroy(ret); ret = metacall("map_style", 1, 2); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 3); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)3); metacall_value_destroy(ret); - void * args[2]; + void *args[2]; args[0] = metacall_value_create_int(5); args[1] = metacall_value_create_int(12); ret = metacallv("get_second", args); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_cast_int(&ret), (int) 12); + EXPECT_EQ((int)metacall_value_cast_int(&ret), (int)12); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { - const char * js_scripts[] = - { + const char *js_scripts[] = { "ducktype.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); - const enum metacall_value_id say_divide_double_ids[] = - { + const enum metacall_value_id say_divide_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("say_divide", say_divide_double_ids, 32.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 8.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)8.0); metacall_value_destroy(ret); - const enum metacall_value_id say_divide_float_ids[] = - { + const enum metacall_value_id say_divide_float_ids[] = { METACALL_FLOAT, METACALL_FLOAT }; ret = metacallt("say_divide", say_divide_float_ids, 32.0f, 4.0f); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((float) metacall_value_cast_float(&ret), (float) 8.0f); + EXPECT_EQ((float)metacall_value_cast_float(&ret), (float)8.0f); metacall_value_destroy(ret); - const enum metacall_value_id some_text_str_ids[] = - { + const enum metacall_value_id some_text_str_ids[] = { METACALL_STRING, METACALL_STRING }; ret = metacallt("some_text", some_text_str_ids, "abc", "def"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "abcdef")); + EXPECT_STREQ(metacall_value_cast_string(&ret), "abcdef"); metacall_value_destroy(ret); - const enum metacall_value_id mixed_double_ids[] = - { + const enum metacall_value_id mixed_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("mixed_js", mixed_double_ids, 1.0, 2.0, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 10.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)10.0); metacall_value_destroy(ret); - const enum metacall_value_id mixed_noreturn_double_ids[] = - { + const enum metacall_value_id mixed_noreturn_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE, METACALL_DOUBLE }; ret = metacallt("mixed_noreturn", mixed_noreturn_double_ids, 1.0, 2.0, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_cast_double(&ret), (double) 10.0); + EXPECT_EQ((double)metacall_value_cast_double(&ret), (double)10.0); metacall_value_destroy(ret); - const enum metacall_value_id mixed_noreturn_float_ids[] = - { + const enum metacall_value_id mixed_noreturn_float_ids[] = { METACALL_FLOAT, METACALL_FLOAT, METACALL_FLOAT, METACALL_FLOAT }; ret = metacallt("mixed_noreturn", mixed_noreturn_float_ids, 1.0f, 2.0f, 3.0f, 4.0f); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((float) metacall_value_cast_float(&ret), (float) 10.0f); + EXPECT_EQ((float)metacall_value_cast_float(&ret), (float)10.0f); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_duplicated_handle_test/CMakeLists.txt b/source/tests/metacall_duplicated_handle_test/CMakeLists.txt index 2cfec5049e..35032c7c5e 100644 --- a/source/tests/metacall_duplicated_handle_test/CMakeLists.txt +++ b/source/tests/metacall_duplicated_handle_test/CMakeLists.txt @@ -1,3 +1,9 @@ + +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + # # Executable name and options # @@ -80,19 +86,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -103,6 +96,9 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/duplicated-in-subfolder-config/metacall.json" ) # @@ -114,11 +110,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +136,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + # # Define test properties # diff --git a/source/tests/metacall_duplicated_handle_test/source/main.cpp b/source/tests/metacall_duplicated_handle_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_duplicated_handle_test/source/main.cpp +++ b/source/tests/metacall_duplicated_handle_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp b/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp index c164d152be..ebd9e57322 100644 --- a/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp +++ b/source/tests/metacall_duplicated_handle_test/source/metacall_duplicated_handle_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,21 +32,78 @@ TEST_F(metacall_duplicated_handle_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_NE((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_NE((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts_0[] = { + "duplicated.js" + }; + + const char *node_scripts_1[] = { + "duplicated-in-subfolder/duplicated.js" + }; + + const enum metacall_value_id double_id[] = { + METACALL_DOUBLE + }; + + /* First script */ + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts_0, sizeof(node_scripts_0) / sizeof(node_scripts_0[0]), NULL)); + + void *ret = metacallt("three_times", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)9.0); + + metacall_value_destroy(ret); + + /* Second script */ + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts_1, sizeof(node_scripts_1) / sizeof(node_scripts_1[0]), NULL)); + + ret = metacallt("two_times", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + /* Script with config */ + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(METACALL_TEST_CONFIG_PATH, NULL, config_allocator)); + + ret = metacallt("one_time", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + metacall_allocator_destroy(config_allocator); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); } diff --git a/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt b/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt new file mode 100644 index 0000000000..c5612d9658 --- /dev/null +++ b/source/tests/metacall_duplicated_symbols_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-duplicated-symbols-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_duplicated_symbols_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_duplicated_symbols_test/source/main.cpp b/source/tests/metacall_duplicated_symbols_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_duplicated_symbols_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp b/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp new file mode 100644 index 0000000000..07fb659c97 --- /dev/null +++ b/source/tests/metacall_duplicated_symbols_test/source/metacall_duplicated_symbols_test.cpp @@ -0,0 +1,159 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_duplicated_symbols_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_duplicated_symbols_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char bufferA[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + static const char bufferB[] = + "#!/usr/bin/env python3\n" + "def multmem(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", bufferA, sizeof(bufferA), NULL)); + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("py", bufferB, sizeof(bufferB), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_ducktype_scripts[] = { + "ducktype.rb" + }; + + const char *rb_second_scripts[] = { + "second.rb" + }; + + /* Both scripts have get_second function which should fail when trying to load */ + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_ducktype_scripts, sizeof(rb_ducktype_scripts) / sizeof(rb_ducktype_scripts[0]), NULL)); + + EXPECT_EQ((int)1, (int)metacall_load_from_file("rb", rb_second_scripts, sizeof(rb_second_scripts) / sizeof(rb_second_scripts[0]), NULL)); + + void *handleA = NULL; + + static const char bufferA[] = + "def get_second(first, second)\n" + " puts('Second value is', second)\n" + " return 4\n" + "end\n"; + + void *handleB = NULL; + + static const char bufferB[] = + "def get_second(first, second)\n" + " puts('Second value is', second)\n" + " return 6\n" + "end\n"; + + const enum metacall_value_id say_multiply_int_ids[] = { + METACALL_INT, METACALL_INT + }; + + /* Test global scope get_second which belongs to ducktype.rb */ + + void *ret = metacallt("get_second", say_multiply_int_ids, 5, 7); + + EXPECT_EQ((int)7, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", bufferA, sizeof(bufferA), &handleA)); + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", bufferB, sizeof(bufferB), &handleB)); + + void *args[2] = { + metacall_value_create_int(234), + metacall_value_create_int(432) + }; + + /* Test handleA get_second which belongs to bufferA */ + + ret = metacallhv_s(handleA, "get_second", args, 2); + + EXPECT_EQ((int)4, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + /* Test handleB get_second which belongs to bufferB */ + + ret = metacallhv_s(handleB, "get_second", args, 2); + + EXPECT_EQ((int)6, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* Python + Ruby */ +#if defined(OPTION_BUILD_LOADERS_PY) && defined(OPTION_BUILD_LOADERS_RB) + { + /* Test duplicated symbols between languages */ + static const char bufferA[] = + "#!/usr/bin/env python3\n" + "def betweenlangs(left: int, right: int) -> int:\n" + "\tresult = left * right;\n" + "\tprint(left, ' * ', right, ' = ', result);\n" + "\treturn result;"; + + static const char bufferB[] = + "def betweenlangs(first, second)\n" + " puts('Second value is', second)\n" + " return 6\n" + "end\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", bufferA, sizeof(bufferA), NULL)); + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("rb", bufferB, sizeof(bufferB), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY + OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_dynlink_path_test/CMakeLists.txt b/source/tests/metacall_dynlink_path_test/CMakeLists.txt new file mode 100644 index 0000000000..c1c9a76c42 --- /dev/null +++ b/source/tests/metacall_dynlink_path_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-dynlink-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_dynlink_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include + + $ # MetaCall includes +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +if(WIN32) + string(REPLACE "/" "\\\\" METACALL_LIBRARY_PATH "${PROJECT_OUTPUT_DIR}") +else() + set(METACALL_LIBRARY_PATH "${PROJECT_OUTPUT_DIR}") +endif() + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + # For metacall library path test + METACALL_LIBRARY_PATH="${METACALL_LIBRARY_PATH}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_dynlink_path_test/source/main.cpp b/source/tests/metacall_dynlink_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_dynlink_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_dynlink_path_test/source/metacall_dynlink_path_test.cpp b/source/tests/metacall_dynlink_path_test/source/metacall_dynlink_path_test.cpp new file mode 100644 index 0000000000..a989448f45 --- /dev/null +++ b/source/tests/metacall_dynlink_path_test/source/metacall_dynlink_path_test.cpp @@ -0,0 +1,55 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +class metacall_dynlink_path_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_dynlink_path_test, DefaultConstructor) +{ + metacall_print_info(); + + dynlink_path path; + + const char name[] = "metacall" +#if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) + "d" +#endif + ; + + size_t length = 0; + + printf(METACALL_LIBRARY_PATH "\n"); + fflush(stdout); + + ASSERT_EQ((int)0, (int)dynlink_library_path(name, path, &length)); + + printf("%s == %s\n", path, METACALL_LIBRARY_PATH); + fflush(stdout); + + ASSERT_EQ((int)0, (int)portability_path_compare(path, METACALL_LIBRARY_PATH)); +} diff --git a/source/tests/metacall_ext_test/CMakeLists.txt b/source/tests/metacall_ext_test/CMakeLists.txt new file mode 100644 index 0000000000..3c6b986981 --- /dev/null +++ b/source/tests/metacall_ext_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_EXT) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ext-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ext_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ext_test/source/main.cpp b/source/tests/metacall_ext_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_ext_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ext_test/source/metacall_ext_test.cpp b/source/tests/metacall_ext_test/source/metacall_ext_test.cpp new file mode 100644 index 0000000000..dde1efd0f7 --- /dev/null +++ b/source/tests/metacall_ext_test/source/metacall_ext_test.cpp @@ -0,0 +1,79 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_ext_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_ext_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + const char *ext_scripts[] = { + "sum_extension" /* The library extension (dll, so, dylib) is crossplatform so we should not add it here */ + }; + + void *handle = NULL; + + /* Test reload of the extension */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ext", ext_scripts, sizeof(ext_scripts) / sizeof(ext_scripts[0]), &handle)); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + + /* Test extension load and call */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ext", ext_scripts, sizeof(ext_scripts) / sizeof(ext_scripts[0]), NULL)); + + void *ret = metacall("sum", 3, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); + + metacall_value_destroy(ret); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_file_fail_test/CMakeLists.txt b/source/tests/metacall_file_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..ab6236fe16 --- /dev/null +++ b/source/tests/metacall_file_fail_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_FILE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-file-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_file_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + file_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_file_fail_test/source/main.cpp b/source/tests/metacall_file_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_file_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_file_fail_test/source/metacall_file_fail_test.cpp b/source/tests/metacall_file_fail_test/source/metacall_file_fail_test.cpp new file mode 100644 index 0000000000..426e44002d --- /dev/null +++ b/source/tests/metacall_file_fail_test/source/metacall_file_fail_test.cpp @@ -0,0 +1,73 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_file_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_file_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* File */ +#if defined(OPTION_BUILD_LOADERS_FILE) + { + const char *scripts[] = { + "this-file-does-not-exists.yeet" + }; + + const size_t size = sizeof(scripts) / sizeof(scripts[0]); + + EXPECT_NE((int)0, (int)metacall_load_from_file("file", scripts, size, NULL)); + } +#endif /* OPTION_BUILD_LOADERS_FILE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_file_glob_test/CMakeLists.txt b/source/tests/metacall_file_glob_test/CMakeLists.txt new file mode 100644 index 0000000000..ffc5a1c274 --- /dev/null +++ b/source/tests/metacall_file_glob_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_FILE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_FILE) + return() +endif() + +# TODO: This test is not passing properly in Windows. +# Glob is not implemented properly on the file_loader for Windows, +# so we disable it until we support glob properly. +include(Portability) + +if(PROJECT_OS_WIN) + message(WARNING "Loader file_loader with glob support does not work on Windows with MSVC compiler") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-file-glob-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_file_glob_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/metacall-globfile.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + file_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_file_glob_test/source/main.cpp b/source/tests/metacall_file_glob_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_file_glob_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_file_glob_test/source/metacall_file_glob_test.cpp b/source/tests/metacall_file_glob_test/source/metacall_file_glob_test.cpp new file mode 100644 index 0000000000..85377d72d6 --- /dev/null +++ b/source/tests/metacall_file_glob_test/source/metacall_file_glob_test.cpp @@ -0,0 +1,74 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_file_glob_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_file_glob_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(METACALL_TEST_CONFIG_PATH, NULL, config_allocator)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("glob/a.txt")); + EXPECT_NE((void *)NULL, (void *)metacall_function("glob/b.txt")); + EXPECT_EQ((void *)NULL, (void *)metacall_function("glob/c.json")); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_allocator_destroy(config_allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_file_test/CMakeLists.txt b/source/tests/metacall_file_test/CMakeLists.txt new file mode 100644 index 0000000000..9dd2a4fb04 --- /dev/null +++ b/source/tests/metacall_file_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_FILE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_FILE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-file-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_file_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + file_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_file_test/source/main.cpp b/source/tests/metacall_file_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_file_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_file_test/source/metacall_file_test.cpp b/source/tests/metacall_file_test/source/metacall_file_test.cpp new file mode 100644 index 0000000000..15658125fd --- /dev/null +++ b/source/tests/metacall_file_test/source/metacall_file_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_file_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_file_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* File */ +#if defined(OPTION_BUILD_LOADERS_FILE) + { + const char *scripts[] = { + "favicon.ico", + "a/a.txt" + }; + + const size_t size = sizeof(scripts) / sizeof(scripts[0]); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("file", scripts, size, NULL)); + + for (size_t i = 0; i < size; ++i) + { + void *f = metacall_function(scripts[i]); + + EXPECT_NE((void *)NULL, (void *)f); + } + } +#endif /* OPTION_BUILD_LOADERS_FILE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_fork_test/CMakeLists.txt b/source/tests/metacall_fork_test/CMakeLists.txt index 7370d96ad3..90c96bdef3 100644 --- a/source/tests/metacall_fork_test/CMakeLists.txt +++ b/source/tests/metacall_fork_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if detours are enabled -if(NOT OPTION_FORK_SAFE) +if(NOT OPTION_FORK_SAFE OR NOT OPTION_BUILD_DETOURS) return() endif() @@ -85,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + plthook_detour +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_fork_test/source/main.cpp b/source/tests/metacall_fork_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_fork_test/source/main.cpp +++ b/source/tests/metacall_fork_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_fork_test/source/metacall_fork_test.cpp b/source/tests/metacall_fork_test/source/metacall_fork_test.cpp index 9b145c13b1..ab784ff942 100644 --- a/source/tests/metacall_fork_test/source/metacall_fork_test.cpp +++ b/source/tests/metacall_fork_test/source/metacall_fork_test.cpp @@ -1,16 +1,16 @@ /* * MetaCall Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A library for providing a foreign function interface calls. * */ -#include +#include #include -#include +#include class metacall_fork_test : public testing::Test { @@ -21,28 +21,30 @@ static int pre_callback_fired = 0; static int post_callback_fired = 0; #if defined(WIN32) || defined(_WIN32) || \ - defined(__CYGWIN__) || defined(__CYGWIN32__) || \ - defined(__MINGW32__) || defined(__MINGW64__) + defined(__CYGWIN__) || defined(__CYGWIN32__) || \ + defined(__MINGW32__) || defined(__MINGW64__) -#define _WIN32_WINNT 0x0600 -#define WIN32_LEAN_AND_MEAN -#include + #define _WIN32_WINNT 0x0600 + #define WIN32_LEAN_AND_MEAN + #include -#define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 -#define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 -#define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 + #define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 + #define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 + #define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 -#define RTL_CLONE_PARENT 0 -#define RTL_CLONE_CHILD 297 + #define RTL_CLONE_PARENT 0 + #define RTL_CLONE_CHILD 297 typedef long NTSTATUS; -typedef struct _CLIENT_ID { +typedef struct _CLIENT_ID +{ PVOID UniqueProcess; PVOID UniqueThread; } CLIENT_ID, *PCLIENT_ID; -typedef struct _SECTION_IMAGE_INFORMATION { +typedef struct _SECTION_IMAGE_INFORMATION +{ PVOID EntryPoint; ULONG StackZeroBits; ULONG StackReserved; @@ -56,7 +58,8 @@ typedef struct _SECTION_IMAGE_INFORMATION { ULONG Unknown2[3]; } SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; -typedef struct _RTL_USER_PROCESS_INFORMATION { +typedef struct _RTL_USER_PROCESS_INFORMATION +{ ULONG Size; HANDLE Process; HANDLE Thread; @@ -64,7 +67,7 @@ typedef struct _RTL_USER_PROCESS_INFORMATION { SECTION_IMAGE_INFORMATION ImageInformation; } RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; -typedef NTSTATUS(NTAPI * RtlCloneUserProcessPtr)(ULONG ProcessFlags, +typedef NTSTATUS(NTAPI *RtlCloneUserProcessPtr)(ULONG ProcessFlags, PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, HANDLE DebugPort, @@ -99,10 +102,7 @@ pid_t fork() if (result == RTL_CLONE_PARENT) { - HANDLE me = GetCurrentProcess(); - pid_t child_pid; - - child_pid = GetProcessId(process_info.Process); + pid_t child_pid = GetProcessId(process_info.Process); ResumeThread(process_info.Thread); CloseHandle(process_info.Process); @@ -112,7 +112,7 @@ pid_t fork() } else if (result == RTL_CLONE_CHILD) { - /* fix stdio */ + /* Fix stdio */ AllocConsole(); return 0; } @@ -122,22 +122,22 @@ pid_t fork() #endif -int pre_callback_test(void * ctx) +int pre_callback_test(void *ctx) { (void)ctx; - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall pre fork callback test"); + std::cout << "MetaCall pre fork callback test" << std::endl; pre_callback_fired = 1; return 0; } -int post_callback_test(metacall_pid pid, void * ctx) +int post_callback_test(metacall_pid pid, void *ctx) { (void)ctx; - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall post fork callback test %d", (int)pid); + std::cout << "MetaCall post fork callback test " << (int)pid << std::endl; post_callback_fired = 1; @@ -148,21 +148,23 @@ TEST_F(metacall_fork_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + metacall_flags(METACALL_FLAGS_FORK_SAFE); + + ASSERT_EQ((int)0, (int)metacall_initialize()); metacall_fork(&pre_callback_test, &post_callback_test); if (fork() == 0) { - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall fork child"); + std::cout << "MetaCall fork child" << std::endl; } else { - log_write("metacall", LOG_LEVEL_DEBUG, "MetaCall fork parent"); + std::cout << "MetaCall fork parent" << std::endl; } - EXPECT_EQ((int) 1, (int) pre_callback_fired); - EXPECT_EQ((int) 1, (int) post_callback_fired); + EXPECT_EQ((int)1, (int)pre_callback_fired); + EXPECT_EQ((int)1, (int)post_callback_fired); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_function_test/CMakeLists.txt b/source/tests/metacall_function_test/CMakeLists.txt new file mode 100644 index 0000000000..9ac6fe47dc --- /dev/null +++ b/source/tests/metacall_function_test/CMakeLists.txt @@ -0,0 +1,158 @@ +# +# Executable name and options +# + +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# Target name +set(target metacall-function-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_function_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_function_test/source/main.cpp b/source/tests/metacall_function_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_function_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_function_test/source/metacall_function_test.cpp b/source/tests/metacall_function_test/source/metacall_function_test.cpp new file mode 100644 index 0000000000..8b837151a1 --- /dev/null +++ b/source/tests/metacall_function_test/source/metacall_function_test.cpp @@ -0,0 +1,276 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +void *c_callback(size_t argc, void *args[], void *data) +{ + (void)argc; + (void)args; + (void)data; + + printf("Callback without args executed\n"); + + return metacall_value_create_long(32L); +} + +void *c_callback_with_args(size_t argc, void *args[], void *data) +{ + long left = metacall_value_to_long(args[0]), right = metacall_value_to_long(args[1]); + + (void)argc; + (void)data; + + printf("Callback with args executed (%ld, %ld)\n", left, right); + + return metacall_value_create_long(left + right); +} + +void *c_callback_factorial_impl(size_t argc, void *args[], void *data) +{ + (void)argc; + (void)data; + + if (metacall_value_to_long(args[0]) <= 0) + { + return metacall_value_create_long(0L); + } + else + { + return metacall_value_copy(args[0]); + } +} + +void *c_callback_factorial(size_t argc, void *args[], void *data) +{ + void *c = metacall_value_to_function(args[0]); + + // function c_callback_factorial(c) { + // return function c_callback_factorial_impl(v) { return v <= 0 ? 1 : v }; + // } + + (void)c; + (void)argc; + (void)data; + + return metacall_value_copy(data); +} + +class metacall_function_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_function_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Native register */ + ASSERT_EQ((int)0, (int)metacall_register("c_callback", c_callback, NULL, METACALL_LONG, 0)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback_with_args", c_callback_with_args, NULL, METACALL_LONG, 2, METACALL_LONG, METACALL_LONG)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback_factorial_impl", c_callback_factorial_impl, NULL, METACALL_LONG, 1, METACALL_LONG)); + ASSERT_EQ((int)0, (int)metacall_register("c_callback_factorial", c_callback_factorial, NULL, METACALL_FUNCTION, 1, METACALL_FUNCTION)); + + /* Create function types */ + void *c_callback_value = metacall_value_create_function(metacall_function("c_callback")); + void *c_callback_with_args_value = metacall_value_create_function(metacall_function("c_callback_with_args")); + void *c_callback_factorial_impl_value = metacall_value_create_function(metacall_function("c_callback_factorial_impl")); + void *c_callback_factorial_value = metacall_value_create_function_closure(metacall_function("c_callback_factorial"), c_callback_factorial_impl_value); + + ASSERT_NE((void *)NULL, (void *)c_callback_value); + ASSERT_NE((void *)NULL, (void *)c_callback_with_args_value); + ASSERT_NE((void *)NULL, (void *)c_callback_factorial_impl_value); + ASSERT_NE((void *)NULL, (void *)c_callback_factorial_value); + + /* Test function data */ + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback"))); + EXPECT_EQ((size_t)0, (size_t)metacall_function_size(metacall_function("c_callback"))); + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback_with_args"))); + EXPECT_EQ((size_t)2, (size_t)metacall_function_size(metacall_function("c_callback_with_args"))); + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback_factorial_impl"))); + EXPECT_EQ((size_t)1, (size_t)metacall_function_size(metacall_function("c_callback_factorial_impl"))); + EXPECT_EQ((int)0, (int)metacall_function_async(metacall_function("c_callback_factorial"))); + EXPECT_EQ((size_t)1, (size_t)metacall_function_size(metacall_function("c_callback_factorial"))); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "function.py" + }; + + void *ret = NULL, *cb_ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + void *function_args[] = { + c_callback_value, + }; + + ret = metacallv("function_cb", function_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)32L); + + metacall_value_destroy(ret); + + void *function_with_args_args[] = { + c_callback_with_args_value, + metacall_value_create_long(5L), + metacall_value_create_long(9L) + }; + + ret = metacallv("function_with_args", function_with_args_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)14L); + + metacall_value_destroy(ret); + + metacall_value_destroy(function_with_args_args[1]); + metacall_value_destroy(function_with_args_args[2]); + + void *function_ret_lambda_args[] = { + metacall_value_create_long(5L) + }; + + ret = metacallv("function_ret_lambda", function_ret_lambda_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + void *f = metacall_value_to_function(ret); + + cb_ret = metacallfv(f, function_ret_lambda_args); + + EXPECT_EQ((long)metacall_value_to_long(cb_ret), (long)25L); + + metacall_value_destroy(cb_ret); + + metacall_value_destroy(ret); + + metacall_value_destroy(function_ret_lambda_args[0]); + + ret = metacallv("function_return", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + f = metacall_value_to_function(ret); + + cb_ret = metacallfv(f, metacall_null_args); + + EXPECT_EQ((long)metacall_value_to_long(cb_ret), (long)4L); + + metacall_value_destroy(cb_ret); + + metacall_value_destroy(ret); + + ret = metacallv("function_pass", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); + + ret = metacallv("function_myclass_new_class", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(ret)); + + void *function_myclass_method_args[] = { + ret + }; + + ret = metacallv("function_myclass_method", function_myclass_method_args); + + metacall_value_destroy(function_myclass_method_args[0]); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_STREQ("hello world", metacall_value_to_string(ret)); + + metacall_value_destroy(ret); + + /* Test Complex Callbakcs */ + void *function_factorial_args[] = { + c_callback_factorial_value + }; + + ret = metacallv("function_factorial", function_factorial_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_FUNCTION, (enum metacall_value_id)metacall_value_id(ret)); + + void *fact = metacall_value_to_function(ret); + + void *cb_function_factorial_args[] = { + metacall_value_create_long(12L) + }; + + cb_ret = metacallfv(fact, cb_function_factorial_args); + + EXPECT_NE((void *)NULL, (void *)cb_ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(cb_ret)); + + /* TODO: I have no clue why this returns 132, the correct value for factorial of 12 is 479001600L */ + EXPECT_EQ((long)132L, (long)metacall_value_to_long(cb_ret)); + + metacall_value_destroy(cb_function_factorial_args[0]); + + metacall_value_destroy(ret); + + metacall_value_destroy(cb_ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* TODO */ + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Clear function values */ + metacall_value_destroy(c_callback_value); + metacall_value_destroy(c_callback_with_args_value); + metacall_value_destroy(c_callback_factorial_impl_value); + metacall_value_destroy(c_callback_factorial_value); + + metacall_destroy(); +} diff --git a/source/tests/metacall_handle_export_test/CMakeLists.txt b/source/tests/metacall_handle_export_test/CMakeLists.txt new file mode 100644 index 0000000000..fb1a708563 --- /dev/null +++ b/source/tests/metacall_handle_export_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-handle-export-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_handle_export_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Dependecies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_handle_export_test/source/main.cpp b/source/tests/metacall_handle_export_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_handle_export_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp b/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp new file mode 100644 index 0000000000..3d1afe184a --- /dev/null +++ b/source/tests/metacall_handle_export_test/source/metacall_handle_export_test.cpp @@ -0,0 +1,143 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_handle_export_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_handle_export_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "example.py" + }; + + void *v, *handle = NULL; + + char *value_str = NULL; + + size_t size = 0; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), &handle)); + + ASSERT_NE((void *)NULL, (void *)handle); + + v = metacall_handle_export(handle); + + EXPECT_NE((void *)NULL, (void *)v); + + value_str = metacall_serialize(metacall_serial(), v, &size, allocator); + + EXPECT_NE((char *)NULL, (char *)value_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << value_str << std::endl; + + metacall_value_destroy(v); + + metacall_allocator_free(allocator, value_str); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + void *v, *handle = NULL; + + char *value_str = NULL; + + size_t size = 0; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), &handle)); + + ASSERT_NE((void *)NULL, (void *)handle); + + v = metacall_handle_export(handle); + + EXPECT_NE((void *)NULL, (void *)v); + + value_str = metacall_serialize(metacall_serial(), v, &size, allocator); + + EXPECT_NE((char *)NULL, (char *)value_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << value_str << std::endl; + + metacall_value_destroy(v); + + metacall_allocator_free(allocator, value_str); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Test bad handle allocation */ + { + struct + { + int random; + int broken; + int handle; + int yeet; + char padding[600]; + } broken_handle = { 0, 0, 0, 0, { 0 } }; + + EXPECT_EQ((void *)NULL, (void *)metacall_handle_export((void *)&broken_handle)); + } + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_handle_get_test/CMakeLists.txt b/source/tests/metacall_handle_get_test/CMakeLists.txt new file mode 100644 index 0000000000..c99af8c34e --- /dev/null +++ b/source/tests/metacall_handle_get_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-handle-get-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_handle_get_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Dependecies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_handle_get_test/source/main.cpp b/source/tests/metacall_handle_get_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_handle_get_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_handle_get_test/source/metacall_handle_get_test.cpp b/source/tests/metacall_handle_get_test/source/metacall_handle_get_test.cpp new file mode 100644 index 0000000000..2e80130384 --- /dev/null +++ b/source/tests/metacall_handle_get_test/source/metacall_handle_get_test.cpp @@ -0,0 +1,162 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_handle_get_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_handle_get_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), &handle)); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *func = metacall_handle_function(handle, "call_test"); + + ASSERT_NE((void *)NULL, (void *)func); + + const enum metacall_value_id double_ids[] = { + METACALL_DOUBLE, METACALL_DOUBLE + }; + + void *ret = metacallht_s(handle, "call_test", double_ids, 2, 10.0, 2.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); + + metacall_value_destroy(ret); + + void *args[] = { + metacall_value_create_double(255.0), + metacall_value_create_double(5.0) + }; + + ret = metacallhv(handle, "call_test", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)1275.0); + + metacall_value_destroy(ret); + + ret = metacallhv_s(handle, "call_test", args, sizeof(args) / sizeof(args[0])); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)1275.0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts_s1[] = { + "s1.py" + }; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts_s1, sizeof(py_scripts_s1) / sizeof(py_scripts_s1[0]), &handle1)); + + ASSERT_NE((void *)NULL, (void *)handle1); + + void *func = metacall_handle_function(handle1, "shared_in_s1_and_s2"); + + ASSERT_NE((void *)NULL, (void *)func); + + void *ret = metacallhv_s(handle1, "shared_in_s1_and_s2", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "Hello from s1"); + + metacall_value_destroy(ret); + + const char *py_scripts_s2[] = { + "s2.py" + }; + + void *handle2 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts_s2, sizeof(py_scripts_s2) / sizeof(py_scripts_s2[0]), &handle2)); + + ASSERT_NE((void *)NULL, (void *)handle2); + + func = metacall_handle_function(handle2, "shared_in_s1_and_s2"); + + ASSERT_NE((void *)NULL, (void *)func); + + ret = metacallhv_s(handle2, "shared_in_s1_and_s2", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "Hello from s2"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_init_fini_test/CMakeLists.txt b/source/tests/metacall_init_fini_test/CMakeLists.txt index bfb277a407..c757cffb72 100644 --- a/source/tests/metacall_init_fini_test/CMakeLists.txt +++ b/source/tests/metacall_init_fini_test/CMakeLists.txt @@ -1,9 +1,5 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) return() endif() @@ -63,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -138,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_init_fini_test/source/main.cpp b/source/tests/metacall_init_fini_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_init_fini_test/source/main.cpp +++ b/source/tests/metacall_init_fini_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp b/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp index 5beb829d7d..b7c64132b2 100644 --- a/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp +++ b/source/tests/metacall_init_fini_test/source/metacall_init_fini_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,29 +32,28 @@ TEST_F(metacall_init_fini_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "initfini.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = NULL; + void *ret = NULL; ret = metacall("intermediate_function"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 15, (int) metacall_value_cast_int(&ret)); + EXPECT_EQ((int)15, (int)metacall_value_cast_int(&ret)); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_initialize_destroy_multiple_node_test/CMakeLists.txt b/source/tests/metacall_initialize_destroy_multiple_node_test/CMakeLists.txt new file mode 100644 index 0000000000..9b06663fda --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_node_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-initialize-destroy-multiple-node-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_initialize_destroy_multiple_node_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_initialize_destroy_multiple_node_test/source/main.cpp b/source/tests/metacall_initialize_destroy_multiple_node_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_node_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_initialize_destroy_multiple_node_test/source/metacall_initialize_destroy_multiple_node_test.cpp b/source/tests/metacall_initialize_destroy_multiple_node_test/source/metacall_initialize_destroy_multiple_node_test.cpp new file mode 100644 index 0000000000..3435254d1f --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_node_test/source/metacall_initialize_destroy_multiple_node_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_initialize_destroy_multiple_node_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_initialize_destroy_multiple_node_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = { + "console.log('a')" + }; + + static const char tag[] = "node"; + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + + EXPECT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + ASSERT_EQ((int)0, (int)metacall_is_initialized(tag)); + + metacall_destroy(); + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + + metacall_destroy(); +} diff --git a/source/tests/metacall_initialize_destroy_multiple_test/CMakeLists.txt b/source/tests/metacall_initialize_destroy_multiple_test/CMakeLists.txt new file mode 100644 index 0000000000..3424ea8817 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-initialize-destroy-multiple-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_initialize_destroy_multiple_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_initialize_destroy_multiple_test/source/main.cpp b/source/tests/metacall_initialize_destroy_multiple_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_initialize_destroy_multiple_test/source/metacall_initialize_destroy_multiple_test.cpp b/source/tests/metacall_initialize_destroy_multiple_test/source/metacall_initialize_destroy_multiple_test.cpp new file mode 100644 index 0000000000..8a57d13521 --- /dev/null +++ b/source/tests/metacall_initialize_destroy_multiple_test/source/metacall_initialize_destroy_multiple_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_initialize_destroy_multiple_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_initialize_destroy_multiple_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) + { + static const char *mock_scripts[] = { + "empty.mock" + }; + + static const char tag[] = "mock"; + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + + EXPECT_EQ((int)0, (int)metacall_load_from_file(tag, mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + + ASSERT_EQ((int)0, (int)metacall_is_initialized(tag)); + + metacall_destroy(); + + ASSERT_EQ((int)1, (int)metacall_is_initialized(tag)); + } +#endif /* OPTION_BUILD_LOADERS_MOCK */ + + metacall_destroy(); + + metacall_destroy(); +} diff --git a/source/tests/metacall_initialize_ex_test/CMakeLists.txt b/source/tests/metacall_initialize_ex_test/CMakeLists.txt index 26ba563bd3..714d7ce5b3 100644 --- a/source/tests/metacall_initialize_ex_test/CMakeLists.txt +++ b/source/tests/metacall_initialize_ex_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + # # Executable name and options # @@ -80,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_initialize_ex_test/source/main.cpp b/source/tests/metacall_initialize_ex_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_initialize_ex_test/source/main.cpp +++ b/source/tests/metacall_initialize_ex_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp b/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp index a1413880eb..d2d9a2ce0a 100644 --- a/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp +++ b/source/tests/metacall_initialize_ex_test/source/metacall_initialize_ex_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -30,42 +30,36 @@ class metacall_initialize_ex_test : public testing::Test TEST_F(metacall_initialize_ex_test, DefaultConstructor) { - #if defined(OPTION_BUILD_LOADERS_MOCK) - static char loader_name[] = "mock"; - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#if defined(OPTION_BUILD_LOADERS_MOCK) + static char loader_name[] = "mock"; +#endif /* OPTION_BUILD_LOADERS_MOCK */ - static struct metacall_initialize_configuration_type initialize_config[] = - { - #if defined(OPTION_BUILD_LOADERS_MOCK) - { - loader_name, NULL - }, - #endif /* OPTION_BUILD_LOADERS_MOCK */ + static struct metacall_initialize_configuration_type initialize_config[] = { +#if defined(OPTION_BUILD_LOADERS_MOCK) + { loader_name, NULL }, +#endif /* OPTION_BUILD_LOADERS_MOCK */ - { - NULL, NULL - } + { NULL, NULL } }; metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize_ex(initialize_config)); + ASSERT_EQ((int)0, (int)metacall_initialize_ex(initialize_config)); - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - ASSERT_EQ((int) 1, (int) metacall_is_initialized(loader_name)); + ASSERT_EQ((int)1, (int)metacall_is_initialized(loader_name)); - EXPECT_EQ((int) 0, (int) metacall_load_from_file(loader_name, mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file(loader_name, mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); - ASSERT_EQ((int) 0, (int) metacall_is_initialized(loader_name)); + ASSERT_EQ((int)0, (int)metacall_is_initialized(loader_name)); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - ASSERT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_initialize_test/CMakeLists.txt b/source/tests/metacall_initialize_test/CMakeLists.txt index 6700ddf1ea..e66bf472e5 100644 --- a/source/tests/metacall_initialize_test/CMakeLists.txt +++ b/source/tests/metacall_initialize_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + # # Executable name and options # @@ -80,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_initialize_test/source/main.cpp b/source/tests/metacall_initialize_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_initialize_test/source/main.cpp +++ b/source/tests/metacall_initialize_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp b/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp index 7238eed697..598f6f1ea6 100644 --- a/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp +++ b/source/tests/metacall_initialize_test/source/metacall_initialize_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,23 +32,22 @@ TEST_F(metacall_initialize_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - ASSERT_EQ((int) 1, (int) metacall_is_initialized("mock")); + ASSERT_EQ((int)1, (int)metacall_is_initialized("mock")); - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); - ASSERT_EQ((int) 0, (int) metacall_is_initialized("mock")); + ASSERT_EQ((int)0, (int)metacall_is_initialized("mock")); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - ASSERT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_inspect_test/CMakeLists.txt b/source/tests/metacall_inspect_test/CMakeLists.txt index e1c97617f0..8a97a0c8b8 100644 --- a/source/tests/metacall_inspect_test/CMakeLists.txt +++ b/source/tests/metacall_inspect_test/CMakeLists.txt @@ -1,12 +1,3 @@ -# -# Setup distributable environment -# - -# Check if distributable libs are enabled -if(NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - # # Executable name and options # @@ -63,9 +54,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -98,9 +88,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -121,11 +109,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -134,10 +131,47 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13749) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #9 metacall_inspect_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp:101 (metacall-inspect-testd+0x20eaa) + # #10 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-inspect-testd+0x54156) + # #11 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + cs_loader + jsm_loader + js_loader + mock_loader + c_loader +) + # # Define test properties # @@ -146,6 +180,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x600000590 pc=0x7fd0975400f0 sp=0x7fd091d32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_inspect_test/source/main.cpp b/source/tests/metacall_inspect_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_inspect_test/source/main.cpp +++ b/source/tests/metacall_inspect_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp b/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp index 6edb829765..36e95f5f7d 100644 --- a/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp +++ b/source/tests/metacall_inspect_test/source/metacall_inspect_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,13 @@ * */ -#include +#include #include #include +#include + class metacall_inspect_test : public testing::Test { public: @@ -32,91 +34,84 @@ TEST_F(metacall_inspect_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "hello.rb", "second.rb" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript SpiderMonkey */ - #if defined(OPTION_BUILD_LOADERS_JSM) +/* JavaScript SpiderMonkey */ +#if defined(OPTION_BUILD_LOADERS_JSM) { - const char * jsm_scripts[] = - { + const char *jsm_scripts[] = { "spider.jsm" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_JSM */ +#endif /* OPTION_BUILD_LOADERS_JSM */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { - const char * js_scripts[] = - { + const char *js_scripts[] = { "divide.js", "third.js" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - /* C# Netcore */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* C# Netcore */ +#if defined(OPTION_BUILD_LOADERS_CS) { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ - /* C */ - #if defined(OPTION_BUILD_LOADERS_C) +/* C */ +#if defined(OPTION_BUILD_LOADERS_C) { - const char * c_scripts[] = - { + const char *c_scripts[] = { "compiled.c" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_C */ +#endif /* OPTION_BUILD_LOADERS_C */ /* Print inspect information */ { @@ -124,19 +119,20 @@ TEST_F(metacall_inspect_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); - char * inspect_str = metacall_inspect(&size, allocator); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_GT((size_t)size, (size_t)0); - EXPECT_GT((size_t)size, (size_t) 0); + printf("%s\n", inspect_str); metacall_allocator_free(allocator, inspect_str); metacall_allocator_destroy(allocator); } - - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_integration_test/CMakeLists.txt b/source/tests/metacall_integration_test/CMakeLists.txt index bf54e50a05..bf2cc13b9f 100644 --- a/source/tests/metacall_integration_test/CMakeLists.txt +++ b/source/tests/metacall_integration_test/CMakeLists.txt @@ -92,13 +92,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::configuration ${META_PROJECT_NAME}::metacall ) @@ -120,11 +113,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -133,10 +135,51 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13698) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 environment::SetUp() /usr/local/metacall/source/tests/metacall_integration_test/source/environment.cpp:34 (metacall-integration-testd+0x21967) + # #18 testing::internal::UnitTestImpl::RunAllTests() (metacall-integration-testd+0x49791) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + cs_loader + py_loader +) + # # Define test properties # @@ -145,6 +188,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x600000690 pc=0x7f3a7b6710f0 sp=0x7f3a75e32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp b/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp index 7aeff0c853..b2efb0121c 100644 --- a/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp +++ b/source/tests/metacall_integration_test/include/metacall-integration-test/environment.hpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,9 @@ * */ -#include +#include -#include - -class environment : public testing::Environment +class environment : public testing::Environment { public: void SetUp(); diff --git a/source/tests/metacall_integration_test/source/environment.cpp b/source/tests/metacall_integration_test/source/environment.cpp index 0a7433b2d7..810c4af687 100644 --- a/source/tests/metacall_integration_test/source/environment.cpp +++ b/source/tests/metacall_integration_test/source/environment.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,22 @@ */ #include -#include +#include #include void environment::SetUp() { - const char * py_scripts[] = { "example.py" }; - const char * cs_scripts[] = { "hello.cs" }; + const char *py_scripts[] = { "example.py" }; + const char *cs_scripts[] = { "hello.cs" }; - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); } void environment::TearDown() { - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_integration_test/source/main.cpp b/source/tests/metacall_integration_test/source/main.cpp index 7d6b8f04cb..7205061c4c 100644 --- a/source/tests/metacall_integration_test/source/main.cpp +++ b/source/tests/metacall_integration_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ * */ -#include +#include #include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new environment()); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_integration_test/source/metacall_integration_test.cpp b/source/tests/metacall_integration_test/source/metacall_integration_test.cpp index 458191620a..629f2b6672 100644 --- a/source/tests/metacall_integration_test/source/metacall_integration_test.cpp +++ b/source/tests/metacall_integration_test/source/metacall_integration_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for dynamic loading and linking shared objects at run-time. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,10 @@ * */ -#include - -#include +#include #include -#include - -#include - class metacall_integration_test : public testing::Test { protected: @@ -45,29 +39,26 @@ TEST_F(metacall_integration_test, CsSayAny) TEST_F(metacall_integration_test, PyMultiply) { - value ret = NULL; + void *ret = NULL; ret = metacall("multiply", 5, 15); - EXPECT_NE((value)NULL, (value)ret); - - EXPECT_EQ((long)value_to_long(ret), (long)75); - - value_destroy(ret); + EXPECT_NE((void *)NULL, (void *)ret); - log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75L); + metacall_value_destroy(ret); } TEST_F(metacall_integration_test, Sum) { - value ret = NULL; + void *ret = NULL; ret = metacall("Sum", 5, 10); - EXPECT_NE((value)NULL, (value)ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int)value_to_long(ret), (int)15); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)15); - value_destroy(ret); + metacall_value_destroy(ret); } diff --git a/source/tests/metacall_invalid_loader_test/CMakeLists.txt b/source/tests/metacall_invalid_loader_test/CMakeLists.txt new file mode 100644 index 0000000000..cef1167c5a --- /dev/null +++ b/source/tests/metacall_invalid_loader_test/CMakeLists.txt @@ -0,0 +1,143 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-invalid-loader-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_invalid_loader_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_invalid_loader_test/source/main.cpp b/source/tests/metacall_invalid_loader_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_invalid_loader_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_invalid_loader_test/source/metacall_invalid_loader_test.cpp b/source/tests/metacall_invalid_loader_test/source/metacall_invalid_loader_test.cpp new file mode 100644 index 0000000000..3ae84d1469 --- /dev/null +++ b/source/tests/metacall_invalid_loader_test/source/metacall_invalid_loader_test.cpp @@ -0,0 +1,52 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_invalid_loader_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_invalid_loader_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char *invalid_scripts[] = { + "a.invalid" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("invalid", invalid_scripts, sizeof(invalid_scripts) / sizeof(invalid_scripts[0]), NULL)); + + static const char invalid_buffer[] = { + "invalid" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("invalid", invalid_buffer, sizeof(invalid_buffer), NULL)); + + ASSERT_EQ((int)1, (int)metacall_is_initialized("invalid")); + + metacall_destroy(); +} diff --git a/source/tests/metacall_java_test/CMakeLists.txt b/source/tests/metacall_java_test/CMakeLists.txt new file mode 100644 index 0000000000..fb814afc1f --- /dev/null +++ b/source/tests/metacall_java_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_JAVA) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-java-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_java_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + java_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: This test fails when run with sanitizers: + # Tracer caught signal 11: addr=0x100000688 pc=0x7fd2e80790f0 sp=0x7fd2489b7d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable Java support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_java_test/source/main.cpp b/source/tests/metacall_java_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_java_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_java_test/source/metacall_java_test.cpp b/source/tests/metacall_java_test/source/metacall_java_test.cpp new file mode 100644 index 0000000000..5eb6141cc5 --- /dev/null +++ b/source/tests/metacall_java_test/source/metacall_java_test.cpp @@ -0,0 +1,416 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_java_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_java_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Java */ +#if defined(OPTION_BUILD_LOADERS_JAVA) + { + { /* TEST LOAD FROM FILE */ + const char *java_scripts[] = { + "Fibonacci.java", "Test.java" + }; + + static const char tag[] = "java"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, java_scripts, sizeof(java_scripts) / sizeof(java_scripts[0]), NULL)); + } + + { /* TEST STATIC GET, SET AND INVOKE */ + void *myclass = metacall_class("Test"); + + void *constructor_params[] = { + metacall_value_create_bool(false), + metacall_value_create_char('H'), + metacall_value_create_short(200), + metacall_value_create_int(10), + metacall_value_create_long(20000007), + metacall_value_create_float(20.321), + metacall_value_create_double(200.123456789), + metacall_value_create_string("Test String", 11) + }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + ASSERT_NE((void *)NULL, (void *)new_object_v); + + metacall_value_destroy(new_object_v); + + { //GET AND SET + void *param1 = metacall_class_static_get(myclass, "LONG_TEST"); + ASSERT_EQ((long)20000007, (long)metacall_value_to_long(param1)); + metacall_value_destroy(param1); + + int longSet = metacall_class_static_set(myclass, "LONG_TEST", metacall_value_create_long(2354363575)); + ASSERT_EQ((int)0, int(longSet)); + + param1 = metacall_class_static_get(myclass, "LONG_TEST"); + ASSERT_EQ((long)2354363575, (long)metacall_value_to_long(param1)); + metacall_value_destroy(param1); + } + + { //GET AND SET + void *param1 = metacall_class_static_get(myclass, "STRING_TEST"); + ASSERT_EQ((std::string) "Test String", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + + int stringSet = metacall_class_static_set(myclass, "STRING_TEST", metacall_value_create_string("ketangupta34", 12)); + ASSERT_EQ((int)0, int(stringSet)); + + param1 = metacall_class_static_get(myclass, "STRING_TEST"); + ASSERT_EQ((std::string) "ketangupta34", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + } + + { //GET CLASS + void *class_test = metacall_class_static_get(myclass, "CLASS_TEST"); + ASSERT_EQ((void *)metacall_class("Test"), (void *)metacall_value_to_class(class_test)); + metacall_value_destroy(class_test); + } + + { //GET ARRAYS + void *str_test = metacall_class_static_get(myclass, "STRING_TEST_Arr"); + void **str_test_arr = metacall_value_to_array(str_test); + ASSERT_STREQ(metacall_value_to_string(str_test_arr[0]), "Hello"); + ASSERT_STREQ(metacall_value_to_string(str_test_arr[1]), "world"); + metacall_value_destroy(str_test); + + void *class_test = metacall_class_static_get(myclass, "CLASS_TEST_Arr"); + void **class_test_arr = metacall_value_to_array(class_test); + ASSERT_EQ((void *)metacall_class("Test"), (void *)metacall_value_to_class(class_test_arr[0])); + metacall_value_destroy(class_test); + } + + { //Invoke + void *args[] = { + metacall_value_create_string("Metacall", 8), + metacall_value_create_int(8) + }; + void *ret = metacallt_class(myclass, "testFunct", METACALL_INT, args, 2); + ASSERT_EQ((int)10, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } + } + + { /* TEST NON STATIC GET, SET and INVOKE FROM CLASS OBJECT */ + void *myclass = metacall_class("Test"); + + void *constructor_params[] = { + metacall_value_create_int(10), + metacall_value_create_int(20) + }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + void *new_object = metacall_value_to_object(new_object_v); + + { + void *param1 = metacall_object_get(new_object, "INT_NS"); + ASSERT_EQ((int)231, (int)metacall_value_to_int(param1)); + metacall_value_destroy(param1); + + int intobjset = metacall_object_set(new_object, "INT_NS", metacall_value_create_int(4321)); + ASSERT_EQ((int)0, int(intobjset)); + + param1 = metacall_object_get(new_object, "INT_NS"); + ASSERT_EQ((int)4321, (int)metacall_value_to_int(param1)); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_object_get(new_object, "CHAR_NS"); + ASSERT_EQ((char)'Z', (char)metacall_value_to_char(param1)); + metacall_value_destroy(param1); + + int charobjset = metacall_object_set(new_object, "CHAR_NS", metacall_value_create_char('Y')); + ASSERT_EQ((int)0, int(charobjset)); + + param1 = metacall_object_get(new_object, "CHAR_NS"); + ASSERT_EQ((char)'Y', (char)metacall_value_to_char(param1)); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_object_get(new_object, "STRING_NS"); + ASSERT_EQ((std::string) "NS string", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + + int strobjset = metacall_object_set(new_object, "STRING_NS", metacall_value_create_string("UPDATED", 7)); + ASSERT_EQ((int)0, int(strobjset)); + + param1 = metacall_object_get(new_object, "STRING_NS"); + ASSERT_EQ((std::string) "UPDATED", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + } + + { //Non Static function Invoke + void *args[] = { + metacall_value_create_int(8) + }; + void *ret = metacallt_object(new_object, "TestNonStaticInt", METACALL_INT, args, 1); + ASSERT_EQ((int)80, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } + + metacall_value_destroy(new_object_v); + } + + { /* TEST String Array Constructor, GET, SET and Invoke for STATIC */ + void *myclass = metacall_class("Test"); + + const void *constructor_params12[] = { + metacall_value_create_string("Constructor", 11), + metacall_value_create_string("Called", 6) + }; + void *val = metacall_value_create_array(constructor_params12, 2); + void *constructor_params[] = { val }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + ASSERT_NE((void *)NULL, (void *)new_object_v); + + metacall_value_destroy(new_object_v); + + { + void *param1 = metacall_class_static_get(myclass, "INT_TEST_Arr"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((int)30, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)12, (int)metacall_value_to_int(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_array[] = { + metacall_value_create_int(10), + metacall_value_create_int(20), + metacall_value_create_int(30) + }; + void *val = metacall_value_create_array(Array_test_array, 3); + + int intArrTest = metacall_class_static_set(myclass, "INT_TEST_Arr", val); + ASSERT_EQ((int)0, int(intArrTest)); + + param1 = metacall_class_static_get(myclass, "INT_TEST_Arr"); + array = metacall_value_to_array(param1); + EXPECT_EQ((int)10, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)20, (int)metacall_value_to_int(array[1])); + EXPECT_EQ((int)30, (int)metacall_value_to_int(array[2])); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_class_static_get(myclass, "CHAR_TEST_Arr"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((char)'G', (char)metacall_value_to_char(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_char[] = { + metacall_value_create_bool('G'), + metacall_value_create_bool('K') + }; + void *val = metacall_value_create_array(Array_test_char, 2); + + int boolset = metacall_class_static_set(myclass, "CHAR_TEST_Arr", val); + ASSERT_EQ((int)0, int(boolset)); + + param1 = metacall_class_static_get(myclass, "CHAR_TEST_Arr"); + array = metacall_value_to_array(param1); + EXPECT_EQ((char)'K', (char)metacall_value_to_char(array[1])); + + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_class_static_get(myclass, "STRING_TEST_Arr"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((std::string) "Hello", (std::string)metacall_value_to_string(array[0])); + EXPECT_EQ((std::string) "world", (std::string)metacall_value_to_string(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_string[] = { + metacall_value_create_string("abc", 3), + metacall_value_create_string("def", 3), + metacall_value_create_string("ghi", 3) + }; + void *val = metacall_value_create_array(Array_test_string, 3); + + int boolset = metacall_class_static_set(myclass, "STRING_TEST_Arr", val); + ASSERT_EQ((int)0, int(boolset)); + + param1 = metacall_class_static_get(myclass, "STRING_TEST_Arr"); + array = metacall_value_to_array(param1); + EXPECT_EQ((std::string) "abc", (std::string)metacall_value_to_string(array[0])); + EXPECT_EQ((std::string) "def", (std::string)metacall_value_to_string(array[1])); + EXPECT_EQ((std::string) "ghi", (std::string)metacall_value_to_string(array[2])); + + metacall_value_destroy(param1); + } + + { // Testing MAIN function call + void *args[] = { + metacall_value_create_array({}, 0) + }; + + void *ret = metacallv_class(myclass, "main", args, 1); + metacall_value_destroy(ret); + } + + { + const void *Array_test_int[] = { + metacall_value_create_int(2), + metacall_value_create_int(3), + metacall_value_create_int(5) + }; + + void *args[] = { + metacall_value_create_array(Array_test_int, 3) + }; + + void *ret = metacallt_class(myclass, "testIntArrayAdd", METACALL_INT, args, 1); + EXPECT_EQ((int)10, (int)metacall_value_to_int(ret)); + } + + // { + // const void *Array_test_string[] = { + // metacall_value_create_string("aaaa", 4), + // metacall_value_create_string("bbbb", 4), + // metacall_value_create_string("cccc", 4) + // }; + // void *args[] = { + // metacall_value_create_array(Array_test_string, 3) + // }; + + // void *ret = metacallt_class(myclass, "testStringArrayFunction", METACALL_ARRAY, args, 1); + // void **array = metacall_value_to_array(ret); + // EXPECT_EQ((std::string) "test", (std::string)metacall_value_to_string(array[0])); + // EXPECT_EQ((std::string) "worked", (std::string)metacall_value_to_string(array[1])); + // } + } + + { /* TEST Array GET, SET and Invoke for NON STATIC */ + void *myclass = metacall_class("Test"); + + const void *constructor_params12[] = { + metacall_value_create_string("Constructor", 11), + metacall_value_create_string("Called", 6) + }; + void *val = metacall_value_create_array(constructor_params12, 2); + void *constructor_params[] = { val }; + + void *new_object_v = metacall_class_new(myclass, "Test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + void *new_object = metacall_value_to_object(new_object_v); + + { + void *param1 = metacall_object_get(new_object, "INT_TEST_Arr_NS"); + void **array = metacall_value_to_array(param1); + EXPECT_EQ((int)50, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)100, (int)metacall_value_to_int(array[1])); + metacall_value_destroy(param1); + + const void *Array_test_array[] = { + metacall_value_create_int(10), + metacall_value_create_int(20), + metacall_value_create_int(30) + }; + void *val = metacall_value_create_array(Array_test_array, 3); + + int intArrTest = metacall_object_set(new_object, "INT_TEST_Arr_NS", val); + ASSERT_EQ((int)0, int(intArrTest)); + + param1 = metacall_object_get(new_object, "INT_TEST_Arr_NS"); + array = metacall_value_to_array(param1); + EXPECT_EQ((int)10, (int)metacall_value_to_int(array[0])); + EXPECT_EQ((int)20, (int)metacall_value_to_int(array[1])); + EXPECT_EQ((int)30, (int)metacall_value_to_int(array[2])); + metacall_value_destroy(param1); + } + + metacall_value_destroy(new_object_v); + } + + { /* TEST LOAD FROM PACKAGE get, set and invoke for Static and Non Static */ + ASSERT_EQ((int)0, (int)metacall_load_from_package("java", "JarTest.jar", NULL)); + + void *myclass = metacall_class("src.JarTest.TestJar"); + + void *new_object_v = metacall_class_new(myclass, "src.JarTest.TestJar", {}, 0); + + { + void *param = metacall_class_static_get(myclass, "jarTestString"); + ASSERT_EQ((std::string) "This is a static Jar String", (std::string)metacall_value_to_string(param)); + metacall_value_destroy(param); + + int stringSet = metacall_class_static_set(myclass, "jarTestString", metacall_value_create_string("ketangupta34", 12)); + ASSERT_EQ((int)0, int(stringSet)); + + param = metacall_class_static_get(myclass, "jarTestString"); + ASSERT_EQ((std::string) "ketangupta34", (std::string)metacall_value_to_string(param)); + metacall_value_destroy(param); + } + + metacall_value_destroy(new_object_v); + } + + // { // Test load from memory + // static const char buffer[] = + // "public class memoryTest{" + // "public static String memoryString = \"Memory test string\";" + // "}"; + + // EXPECT_EQ((int)0, (int)metacall_load_from_memory("java", buffer, sizeof(buffer), NULL)); + // } + } +#endif /* OPTION_BUILD_LOADERS_JAVA */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_julia_test/CMakeLists.txt b/source/tests/metacall_julia_test/CMakeLists.txt new file mode 100644 index 0000000000..0bfe22f233 --- /dev/null +++ b/source/tests/metacall_julia_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_JL OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_JL) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-julia-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_julia_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + julia_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_julia_test/source/main.cpp b/source/tests/metacall_julia_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_julia_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_julia_test/source/metacall_julia_test.cpp b/source/tests/metacall_julia_test/source/metacall_julia_test.cpp new file mode 100644 index 0000000000..d85bd9ad0d --- /dev/null +++ b/source/tests/metacall_julia_test/source/metacall_julia_test.cpp @@ -0,0 +1,102 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_julia_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_julia_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Julia */ +#if defined(OPTION_BUILD_LOADERS_JL) + { + const char *julia_scripts[] = { + "new.jl" + }; + + const enum metacall_value_id plus_string_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + const size_t args_size = sizeof(plus_string_ids) / sizeof(plus_string_ids[0]); + + static const char tag[] = "jl"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, julia_scripts, sizeof(julia_scripts) / sizeof(julia_scripts[0]), NULL)); + + /* TODO: Uncomment this when calls are implemented */ + /* + void *ret = metacallt_s("plus", hello_string_ids, args_size, 3.4f, 12.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)15.4f); + + metacall_value_destroy(ret); + */ + + /* TODO: Test load from memory */ + /* + static const char buffer[] = + "function plus_ten(a)\n" + " return a + 10\n" + "end\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + */ + + /* TODO: Call to the functions of the load from memory script */ + + /* TODO: Test load from package (in case Julia supports compiled binaries, otherwise delete this line) */ + } +#endif /* OPTION_BUILD_LOADERS_JL */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_library_path_without_env_vars_test/CMakeLists.txt b/source/tests/metacall_library_path_without_env_vars_test/CMakeLists.txt new file mode 100644 index 0000000000..33428a51b8 --- /dev/null +++ b/source/tests/metacall_library_path_without_env_vars_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-library-path-without-env-vars-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_library_path_without_env_vars_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_CONFIGURATION_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_library_path_without_env_vars_test/source/main.cpp b/source/tests/metacall_library_path_without_env_vars_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_library_path_without_env_vars_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_library_path_without_env_vars_test/source/metacall_library_path_without_env_vars_test.cpp b/source/tests/metacall_library_path_without_env_vars_test/source/metacall_library_path_without_env_vars_test.cpp new file mode 100644 index 0000000000..f971670af2 --- /dev/null +++ b/source/tests/metacall_library_path_without_env_vars_test/source/metacall_library_path_without_env_vars_test.cpp @@ -0,0 +1,49 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_library_path_without_env_vars_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_library_path_without_env_vars_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) + { + const char *mock_scripts[] = { + "empty.mock" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_MOCK */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_llvm_test/CMakeLists.txt b/source/tests/metacall_llvm_test/CMakeLists.txt new file mode 100644 index 0000000000..cbe63c93f1 --- /dev/null +++ b/source/tests/metacall_llvm_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_LLVM OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_LLVM) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-llvm-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_llvm_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + llvm_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_llvm_test/source/main.cpp b/source/tests/metacall_llvm_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_llvm_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_llvm_test/source/metacall_llvm_test.cpp b/source/tests/metacall_llvm_test/source/metacall_llvm_test.cpp new file mode 100644 index 0000000000..fe1e961250 --- /dev/null +++ b/source/tests/metacall_llvm_test/source/metacall_llvm_test.cpp @@ -0,0 +1,103 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_llvm_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_llvm_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* LLVM */ +#if defined(OPTION_BUILD_LOADERS_LLVM) + { + const char *llvm_scripts[] = { + "input.ll" + }; + + const enum metacall_value_id hello_string_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + const size_t args_size = sizeof(hello_string_ids) / sizeof(hello_string_ids[0]); + + static const char tag[] = "llvm"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, llvm_scripts, sizeof(llvm_scripts) / sizeof(llvm_scripts[0]), NULL)); + + /* TODO: Uncomment this when calls are implemented */ + /* + void *ret = ret = metacallt_s("adder", hello_string_ids, args_size, 3.4f, 12.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)15.4f); + + metacall_value_destroy(ret); + */ + + /* TODO: Test load from memory */ + /* + static const char buffer[] = + "define dso_local float @whatever(float, float) #0 {\n" + "...\n" + "...\n" + "...\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + */ + + /* TODO: Call to the functions of the load from memory script */ + + /* TODO: Test load from package */ + } +#endif /* OPTION_BUILD_LOADERS_LLVM */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_fail_test/CMakeLists.txt b/source/tests/metacall_load_configuration_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..c49bf0e10d --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/CMakeLists.txt @@ -0,0 +1,160 @@ + +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-load-configuration-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_configuration_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Configuration path + METACALL_TEST_CONFIG_PATH="${CMAKE_CURRENT_SOURCE_DIR}/data/" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-object.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-object.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-object.json @@ -0,0 +1 @@ +{} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-scripts.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-scripts.json new file mode 100644 index 0000000000..b0caa4d695 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty-scripts.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-empty.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-empty.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-without-language-id.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-language-id.json new file mode 100644 index 0000000000..0ddfb1e908 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-language-id.json @@ -0,0 +1,6 @@ +{ + "path": ".", + "scripts": [ + "pluginA.js" + ] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-without-scripts.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-scripts.json new file mode 100644 index 0000000000..cbd571bd89 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-without-scripts.json @@ -0,0 +1,4 @@ +{ + "language_id": "node", + "path": "." +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id-type.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id-type.json new file mode 100644 index 0000000000..f8e6caa10a --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id-type.json @@ -0,0 +1,5 @@ +{ + "language_id": [], + "path": "./js", + "scripts": [] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id.json new file mode 100644 index 0000000000..d00203bc53 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-language-id.json @@ -0,0 +1,7 @@ +{ + "language_id": "", + "path": ".", + "scripts": [ + "pluginA.js" + ] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path-type.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path-type.json new file mode 100644 index 0000000000..bef54c140f --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path-type.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": 3, + "scripts": [] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path.json new file mode 100644 index 0000000000..18da4b3db6 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-path.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": "", + "scripts": [ + "pluginA.js" + ] +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts-type.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts-type.json new file mode 100644 index 0000000000..6ddb5d86b3 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts-type.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": "./js", + "scripts": "hello" +} diff --git a/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts.json b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts.json new file mode 100644 index 0000000000..30bea4e448 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/data/metacall-wrong-scripts.json @@ -0,0 +1,5 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [""] +} diff --git a/source/tests/metacall_load_configuration_fail_test/source/main.cpp b/source/tests/metacall_load_configuration_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_configuration_fail_test/source/metacall_load_configuration_fail_test.cpp b/source/tests/metacall_load_configuration_fail_test/source/metacall_load_configuration_fail_test.cpp new file mode 100644 index 0000000000..0fe69549c7 --- /dev/null +++ b/source/tests/metacall_load_configuration_fail_test/source/metacall_load_configuration_fail_test.cpp @@ -0,0 +1,76 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_configuration_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_configuration_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + #define CONFIG_PATH(path) METACALL_TEST_CONFIG_PATH path + + /* Script with config */ + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-language-id.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-path.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-scripts.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-empty-scripts.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-without-language-id.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-without-scripts.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-empty-object.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-empty.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-scripts-type.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-path-type.json"), NULL, config_allocator)); + + ASSERT_EQ((int)1, (int)metacall_load_from_configuration(CONFIG_PATH("metacall-wrong-language-id-type.json"), NULL, config_allocator)); + + metacall_allocator_destroy(config_allocator); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_node_python_test/CMakeLists.txt b/source/tests/metacall_load_configuration_node_python_test/CMakeLists.txt new file mode 100644 index 0000000000..6653f9010f --- /dev/null +++ b/source/tests/metacall_load_configuration_node_python_test/CMakeLists.txt @@ -0,0 +1,166 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-load-configuration-node-python-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_configuration_node_python_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/auth-function-mesh/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_configuration_node_python_test/source/main.cpp b/source/tests/metacall_load_configuration_node_python_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_configuration_node_python_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_configuration_node_python_test/source/metacall_load_configuration_node_python_test.cpp b/source/tests/metacall_load_configuration_node_python_test/source/metacall_load_configuration_node_python_test.cpp new file mode 100644 index 0000000000..09d50c5931 --- /dev/null +++ b/source/tests/metacall_load_configuration_node_python_test/source/metacall_load_configuration_node_python_test.cpp @@ -0,0 +1,59 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_configuration_node_python_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_configuration_node_python_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + ASSERT_EQ((int)0, (int)metacall_execution_path("py", METACALL_PYTHON_PORT_PATH)); + + static const char buffer[] = + "const { metacall_load_from_configuration_export } = require('" METACALL_NODE_PORT_PATH "');\n" + "const config = metacall_load_from_configuration_export('" METACALL_TEST_CONFIG_PATH "');\n" + "module.exports = { enc: (v) => config.encrypt(v) }\n"; + + static const char tag[] = "node"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + const enum metacall_value_id node_memory_enc_ids[] = { + METACALL_DOUBLE + }; + + void *ret = metacallt("enc", node_memory_enc_ids, 15.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)5.0, (double)metacall_value_to_double(ret)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_python_node_test/CMakeLists.txt b/source/tests/metacall_load_configuration_python_node_test/CMakeLists.txt new file mode 100644 index 0000000000..f8dd16c325 --- /dev/null +++ b/source/tests/metacall_load_configuration_python_node_test/CMakeLists.txt @@ -0,0 +1,163 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-load-configuration-python-node-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_configuration_python_node_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" + + # Configuration path + METACALL_TEST_CONFIG_PATH="${LOADER_SCRIPT_PATH}/auth-function-mesh/metacall.json" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_configuration_python_node_test/source/main.cpp b/source/tests/metacall_load_configuration_python_node_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_configuration_python_node_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_configuration_python_node_test/source/metacall_load_configuration_python_node_test.cpp b/source/tests/metacall_load_configuration_python_node_test/source/metacall_load_configuration_python_node_test.cpp new file mode 100644 index 0000000000..ece10c5b20 --- /dev/null +++ b/source/tests/metacall_load_configuration_python_node_test/source/metacall_load_configuration_python_node_test.cpp @@ -0,0 +1,66 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_configuration_python_node_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_configuration_python_node_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + ASSERT_NE((void *)NULL, (void *)config_allocator); + + ASSERT_EQ((int)0, (int)metacall_execution_path("py", METACALL_PYTHON_PORT_PATH)); + + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(METACALL_TEST_CONFIG_PATH, NULL, config_allocator)); + + void *ret = metacall("encrypt", 15.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)5.0); + + metacall_value_destroy(ret); + + ret = metacall("decrypt", 15.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + metacall_allocator_destroy(config_allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt b/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt index 1bef3e19cd..a16c0c46f7 100644 --- a/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt +++ b/source/tests/metacall_load_configuration_relative_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + # # Executable name and options # @@ -81,19 +86,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -115,11 +107,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,6 +133,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Configure test data # diff --git a/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in b/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in index dcfa438dd8..79701d0f7b 100644 --- a/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in +++ b/source/tests/metacall_load_configuration_relative_test/include/metacall_load_configuration_relative_test/metacall_load_configuration_relative_test.h.in @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/source/tests/metacall_load_configuration_relative_test/source/main.cpp b/source/tests/metacall_load_configuration_relative_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_load_configuration_relative_test/source/main.cpp +++ b/source/tests/metacall_load_configuration_relative_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp b/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp index da80684e02..be9556f5e1 100644 --- a/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp +++ b/source/tests/metacall_load_configuration_relative_test/source/metacall_load_configuration_relative_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,11 @@ #include -#include +#include #include #include -#include - -#include - class metacall_load_configuration_relative_test : public testing::Test { public: @@ -38,35 +34,36 @@ TEST_F(metacall_load_configuration_relative_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - ASSERT_NE((memory_allocator) NULL, (memory_allocator) allocator); + ASSERT_NE((void *)NULL, (void *)config_allocator); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration(RELATIVE_CONFIGURATION_PATH "metacall_load_from_configuration_relative_node_test.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration(RELATIVE_CONFIGURATION_PATH "metacall_load_from_configuration_relative_node_test.json", NULL, config_allocator)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - memory_allocator_destroy(allocator); + metacall_allocator_destroy(config_allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_load_configuration_test/CMakeLists.txt b/source/tests/metacall_load_configuration_test/CMakeLists.txt index 6daa40a694..3c42adeb57 100644 --- a/source/tests/metacall_load_configuration_test/CMakeLists.txt +++ b/source/tests/metacall_load_configuration_test/CMakeLists.txt @@ -80,19 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,15 +127,25 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader + rb_loader +) + # # Configure test data # if(MSVC) - configure_file(data/metacall_load_from_configuration_py_test_a.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_py_test_a.json) - configure_file(data/metacall_load_from_configuration_py_test_b.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_py_test_b.json) - configure_file(data/metacall_load_from_configuration_rb_test.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_rb_test.json) - configure_file(data/metacall_load_from_configuration_node_test.json.in ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/metacall_load_from_configuration_node_test.json) + configure_file(data/metacall_load_from_configuration_py_test_a.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_py_test_a.json) + configure_file(data/metacall_load_from_configuration_py_test_b.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_py_test_b.json) + configure_file(data/metacall_load_from_configuration_rb_test.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_rb_test.json) + configure_file(data/metacall_load_from_configuration_node_test.json.in ${PROJECT_OUTPUT_DIR}/metacall_load_from_configuration_node_test.json) endif() configure_file(data/metacall_load_from_configuration_py_test_a.json.in ${CMAKE_CURRENT_BINARY_DIR}/metacall_load_from_configuration_py_test_a.json) diff --git a/source/tests/metacall_load_configuration_test/source/main.cpp b/source/tests/metacall_load_configuration_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_load_configuration_test/source/main.cpp +++ b/source/tests/metacall_load_configuration_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp b/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp index c2debe2fac..21657b9c8e 100644 --- a/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp +++ b/source/tests/metacall_load_configuration_test/source/metacall_load_configuration_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,11 @@ * */ -#include +#include #include #include -#include - -#include - class metacall_load_configuration_test : public testing::Test { public: @@ -36,28 +32,30 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); + void *config_allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - ASSERT_NE((memory_allocator) NULL, (memory_allocator) allocator); + ASSERT_NE((void *)NULL, (void *)config_allocator); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { const long seven_multiples_limit = 10; long iterator; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_py_test_a.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_py_test_a.json", NULL, config_allocator)); ret = metacall("multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -65,48 +63,56 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) { ret = metacall("multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *)metacall("hello")); + ret = metacall("hello"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_py_test_b.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_py_test_b.json", NULL, config_allocator)); /* Print inspect information */ { @@ -114,13 +120,13 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -131,9 +137,9 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) ret = metacall("s_multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -141,18 +147,18 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) { ret = metacall("s_multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("s_divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); @@ -172,68 +178,81 @@ TEST_F(metacall_load_configuration_test, DefaultConstructor) metacall_value_destroy(ret); - EXPECT_EQ((void *)NULL, (void *)metacall("s_hello")); + ret = metacall("s_hello"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); ret = metacall("s_strcat", "Hello ", "Universe"); EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int)0, (int)strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_rb_test.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_rb_test.json", NULL, config_allocator)); ret = metacall("say_multiply", 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 35); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)35); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall("say_null")); + ret = metacall("say_null"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello meta-programmer!"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_configuration("metacall_load_from_configuration_node_test.json", NULL, allocator)); + ASSERT_EQ((int)0, (int)metacall_load_from_configuration("metacall_load_from_configuration_node_test.json", NULL, config_allocator)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - memory_allocator_destroy(allocator); + metacall_allocator_destroy(config_allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_load_memory_empty_test/CMakeLists.txt b/source/tests/metacall_load_memory_empty_test/CMakeLists.txt new file mode 100644 index 0000000000..b98f18bca6 --- /dev/null +++ b/source/tests/metacall_load_memory_empty_test/CMakeLists.txt @@ -0,0 +1,154 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-load-memory-empty-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_load_memory_empty_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_load_memory_empty_test/source/main.cpp b/source/tests/metacall_load_memory_empty_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_load_memory_empty_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_load_memory_empty_test/source/metacall_load_memory_empty_test.cpp b/source/tests/metacall_load_memory_empty_test/source/metacall_load_memory_empty_test.cpp new file mode 100644 index 0000000000..4adffb8e4a --- /dev/null +++ b/source/tests/metacall_load_memory_empty_test/source/metacall_load_memory_empty_test.cpp @@ -0,0 +1,71 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_load_memory_empty_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_load_memory_empty_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = ""; + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + ASSERT_EQ((int)0, (int)metacall_load_from_memory("ts", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Non existent loader */ + ASSERT_EQ((int)1, (int)metacall_load_from_memory("asdfghjk", buffer, sizeof(buffer), NULL)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_load_memory_test/CMakeLists.txt b/source/tests/metacall_load_memory_test/CMakeLists.txt index 17f1fdb7f7..9203a34ca2 100644 --- a/source/tests/metacall_load_memory_test/CMakeLists.txt +++ b/source/tests/metacall_load_memory_test/CMakeLists.txt @@ -80,19 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +127,16 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + py_loader + rb_loader + js_loader +) + # # Define test properties # diff --git a/source/tests/metacall_load_memory_test/source/main.cpp b/source/tests/metacall_load_memory_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_load_memory_test/source/main.cpp +++ b/source/tests/metacall_load_memory_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp b/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp index 108ace0221..9610ddf73c 100644 --- a/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp +++ b/source/tests/metacall_load_memory_test/source/metacall_load_memory_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,11 @@ * */ -#include +#include #include #include -#include - -#include - class metacall_load_memory_test : public testing::Test { public: @@ -34,19 +30,19 @@ class metacall_load_memory_test : public testing::Test TEST_F(metacall_load_memory_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - metacall_print_info(); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = - "#!/usr/bin/python3.5\n" + "#!/usr/bin/env python3\n" "def multmem(left: int, right: int) -> int:\n" "\tresult = left * right;\n" "\tprint(left, ' * ', right, ' = ', result);\n" @@ -58,35 +54,33 @@ TEST_F(metacall_load_memory_test, DefaultConstructor) long iterator; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); - value ret = NULL; + void *ret = NULL; ret = metacall("multmem", 5, 15); - EXPECT_NE((value) NULL, (value) ret); - - EXPECT_EQ((long) value_to_long(ret), (long) 75); + EXPECT_NE((void *)NULL, (void *)ret); - value_destroy(ret); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); - log_write("metacall", LOG_LEVEL_DEBUG, "5's multiples dude!"); + metacall_value_destroy(ret); for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) { ret = metacall("multmem", 5, iterator); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) value_to_long(ret), (long) (5 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(5 * iterator)); - value_destroy(ret); + metacall_value_destroy(ret); } } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { static const char buffer[] = "#!/usr/bin/ruby\n" @@ -108,30 +102,30 @@ TEST_F(metacall_load_memory_test, DefaultConstructor) static const char extension[] = "rb"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); - value ret = NULL; + void *ret = NULL; ret = metacall("mem_multiply", 5, 5); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) value_to_int(ret), (int) 25); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)25); - value_destroy(ret); + metacall_value_destroy(ret); ret = metacall("comment_line", 15); - EXPECT_EQ((value) NULL, (value) ret); + EXPECT_EQ((void *)NULL, (void *)ret); ret = metacall("comment_multi_line", 25); - EXPECT_EQ((value) NULL, (value) ret); + EXPECT_EQ((void *)NULL, (void *)ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) { static const char buffer[] = "#!/usr/bin/env sh\n" @@ -145,23 +139,23 @@ TEST_F(metacall_load_memory_test, DefaultConstructor) static const char extension[] = "js"; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(extension, buffer, sizeof(buffer), NULL)); - value ret = NULL; + void *ret = NULL; ret = metacall("mem_divide", 10.0, 5.0); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 2.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)2.0); - value_destroy(ret); + metacall_value_destroy(ret); ret = metacall("mem_comment", 10.0); - EXPECT_EQ((value) NULL, (value) ret); + EXPECT_EQ((void *)NULL, (void *)ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_logs_test/CMakeLists.txt b/source/tests/metacall_logs_test/CMakeLists.txt new file mode 100644 index 0000000000..2a56bf6371 --- /dev/null +++ b/source/tests/metacall_logs_test/CMakeLists.txt @@ -0,0 +1,143 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-logs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_logs_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_logs_test/source/main.cpp b/source/tests/metacall_logs_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_logs_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_logs_test/source/metacall_logs_test.cpp b/source/tests/metacall_logs_test/source/metacall_logs_test.cpp new file mode 100644 index 0000000000..179554ab25 --- /dev/null +++ b/source/tests/metacall_logs_test/source/metacall_logs_test.cpp @@ -0,0 +1,44 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_logs_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_logs_test, DefaultConstructor) +{ + metacall_log_stdio_type log_stdio = { stdout }; + + metacall_print_info(); + + for (int i = 0; i < 50; ++i) + { + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + } + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + metacall_destroy(); +} diff --git a/source/tests/metacall_lua_test/CMakeLists.txt b/source/tests/metacall_lua_test/CMakeLists.txt new file mode 100644 index 0000000000..d356553b5c --- /dev/null +++ b/source/tests/metacall_lua_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_LUA OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_LUA) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-lua-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_lua_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + lua_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_lua_test/source/main.cpp b/source/tests/metacall_lua_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_lua_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_lua_test/source/metacall_lua_test.cpp b/source/tests/metacall_lua_test/source/metacall_lua_test.cpp new file mode 100644 index 0000000000..63c84b4375 --- /dev/null +++ b/source/tests/metacall_lua_test/source/metacall_lua_test.cpp @@ -0,0 +1,114 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_lua_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_lua_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Lua */ +#if defined(OPTION_BUILD_LOADERS_LUA) + { + static const char tag[] = "lua"; + + /* Load from memory */ + { + static const char buffer[] = + "function luamin(left, right)\n" + " if (left > right) then\n" + " return right;\n" + " else\n" + " return left;\n" + " end\n" + "end\n"; + + const enum metacall_value_id min_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + void *ret = metacallt_s("luamin", min_ids, 2, 3.0f, 6.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)3.0f); + + metacall_value_destroy(ret); + } + + /* Load from file */ + { + const char *lua_scripts[] = { + "max.lua" + }; + + const enum metacall_value_id max_ids[] = { + METACALL_INT, METACALL_INT + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, lua_scripts, sizeof(lua_scripts) / sizeof(lua_scripts[0]), NULL)); + + void *ret = metacallt_s("luamax", max_ids, 2, 3, 6); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)metacall_value_to_int(ret), (int)6); + + metacall_value_destroy(ret); + } + } +#endif /* OPTION_BUILD_LOADERS_LUA */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_map_await_test/CMakeLists.txt b/source/tests/metacall_map_await_test/CMakeLists.txt new file mode 100644 index 0000000000..6b1df3e956 --- /dev/null +++ b/source/tests/metacall_map_await_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-map-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_map_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_map_await_test/source/main.cpp b/source/tests/metacall_map_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_map_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp b/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp new file mode 100644 index 0000000000..d3f1d4eb76 --- /dev/null +++ b/source/tests/metacall_map_await_test/source/metacall_map_await_test.cpp @@ -0,0 +1,275 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +#include + +std::atomic success_callbacks{}; + +class metacall_map_await_test : public testing::Test +{ +public: +}; + +static void *hello_boy_await_ok(void *result, void *data) +{ + double *it = static_cast(data); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_NE((void *)NULL, (void *)data); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + printf("hello_boy_await future (from map) callback: %f\n", metacall_value_to_double(result)); + + fflush(stdout); + + EXPECT_EQ((double)metacall_value_to_double(result), (double)(7.0 + *it)); + + delete it; + + ++success_callbacks; + + return NULL; +} + +static void *hello_boy_await_fail(void *, void *data) +{ + double *it = static_cast(data); + + int this_should_never_happen = 1; + + EXPECT_NE((void *)NULL, (void *)data); + + delete it; + + EXPECT_NE((int)0, (int)this_should_never_happen); + + return NULL; +} + +static void *hello_world_await_fail(void *, void *data) +{ + int this_should_never_happen = 1; + + EXPECT_EQ((void *)NULL, (void *)data); + + EXPECT_NE((int)0, (int)this_should_never_happen); + + return NULL; +} + +static void *hello_world_await_ok(void *result, void *data) +{ + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((void *)NULL, (void *)data); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_STRING); + + printf("hello_world callback: %s\n", metacall_value_to_string(result)); + + fflush(stdout); + + EXPECT_STREQ(metacall_value_to_string(result), "Hello World"); + + ++success_callbacks; + + return NULL; +} + +TEST_F(metacall_map_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + static const char left[] = "a"; + static const char right[] = "b"; + + void *keys[] = { + metacall_value_create_string(left, sizeof(left) - 1), + metacall_value_create_string(right, sizeof(right) - 1) + }; + + void *values[] = { + metacall_value_create_double(7.0), + metacall_value_create_double(0.0) + }; + + static const char args_map[] = "{\"a\":10,\"b\":2}"; + + void *func = metacall_function("hello_boy_await"); + + ASSERT_NE((void *)NULL, (void *)func); + + /* Call by map using arrays */ + for (double iterator = 0.0; iterator <= 10.0; iterator += 1.0) + { + double *context = new double(iterator); + + values[1] = metacall_value_from_double(values[1], iterator); + + ret = metacallfmv_await(func, keys, values, hello_boy_await_ok, hello_boy_await_fail, (void *)context); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + } + + /* Call by map using serial */ + ret = metacallfms_await( + func, args_map, sizeof(args_map), allocator, [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)metacall_value_to_double(result), (double)12.0); + + printf("hello_boy_await future (from map serial) callback: %f\n", metacall_value_to_double(result)); + + fflush(stdout); + + ++success_callbacks; + + return NULL; + }, + NULL, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + + /* Call by map using serial (segmentation fault on vaule destroy) */ + static const char args_random_map[] = "{\"token\":\"abc\",\"serial\":[\"XYZ\",\"ABC\"],\"range\":\"eee\"}"; + + func = metacall_function("get_random_data"); + + ASSERT_NE((void *)NULL, (void *)func); + + ret = metacallfms_await( + func, args_random_map, sizeof(args_random_map), allocator, [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)metacall_value_to_double(result), (double)12.0); + + printf("get_random_data future (from map serial) callback: %f\n", metacall_value_to_double(result)); + + fflush(stdout); + + ++success_callbacks; + + return NULL; + }, + NULL, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + + /* Call by map using arrays (nested await) */ + func = metacall_function("hello_boy_nested_await"); + + ASSERT_NE((void *)NULL, (void *)func); + + for (double iterator = 0.0; iterator <= 10.0; iterator += 1.0) + { + double *context = new double(iterator); + + values[1] = metacall_value_from_double(values[1], iterator); + + ret = metacallfmv_await(func, keys, values, hello_boy_await_ok, hello_boy_await_fail, (void *)context); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + } + + metacall_value_destroy(keys[0]); + metacall_value_destroy(keys[1]); + + metacall_value_destroy(values[0]); + metacall_value_destroy(values[1]); + + /* Await function that throws */ + ret = metacall_await("throw_await", metacall_null_args, hello_world_await_fail, hello_world_await_ok, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + + /* Await function that returns */ + ret = metacall_await("return_await", metacall_null_args, hello_world_await_ok, hello_world_await_fail, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_allocator_destroy(allocator); + + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* Total amount of successful callbacks must be 26 */ + EXPECT_EQ((int)success_callbacks, (int)26); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ +} diff --git a/source/tests/metacall_map_test/CMakeLists.txt b/source/tests/metacall_map_test/CMakeLists.txt index db90eba196..b9d40bf14a 100644 --- a/source/tests/metacall_map_test/CMakeLists.txt +++ b/source/tests/metacall_map_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS) + return() +endif() + # # Executable name and options # @@ -80,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,6 +132,15 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_map_test/source/main.cpp b/source/tests/metacall_map_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_map_test/source/main.cpp +++ b/source/tests/metacall_map_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_map_test/source/metacall_map_test.cpp b/source/tests/metacall_map_test/source/metacall_map_test.cpp index 843758631a..84d641f4d0 100644 --- a/source/tests/metacall_map_test/source/metacall_map_test.cpp +++ b/source/tests/metacall_map_test/source/metacall_map_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_map_test : public testing::Test { @@ -33,17 +33,16 @@ TEST_F(metacall_map_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py" }; @@ -51,21 +50,19 @@ TEST_F(metacall_map_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); static const char left[] = "left"; static const char right[] = "right"; - void * keys[] = - { + void *keys[] = { metacall_value_create_string(left, sizeof(left) - 1), metacall_value_create_string(right, sizeof(right) - 1) }; - void * values[] = - { + void *values[] = { metacall_value_create_long(7), metacall_value_create_long(0) }; @@ -73,7 +70,9 @@ TEST_F(metacall_map_test, DefaultConstructor) static const char args_map[] = "{\"left\":10,\"right\":2}"; static const char args_array[] = "[10, 2]"; - void * func = metacall_function("multiply"); + void *func = metacall_function("multiply"); + + ASSERT_NE((void *)NULL, (void *)func); /* Call by map using arrays */ for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) @@ -82,9 +81,9 @@ TEST_F(metacall_map_test, DefaultConstructor) ret = metacallfmv(func, keys, values); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } @@ -98,25 +97,118 @@ TEST_F(metacall_map_test, DefaultConstructor) /* Call by map using serial */ ret = metacallfms(func, args_map, sizeof(args_map), allocator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)20); + + metacall_value_destroy(ret); + + /* Call by array using serial */ + ret = metacallfs(func, args_array, sizeof(args_array), allocator); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((long)metacall_value_to_long(ret), (long)20); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + const enum metacall_value_id double_ids[] = { + METACALL_DOUBLE, METACALL_DOUBLE + }; + + static const char args_map[] = "{\"a\":10,\"b\":2}"; + static const char args_bad_map[] = "{a:10,b:2}"; + static const char args_array[] = "[10, 2]"; + static const char args_bad_array[] = "[10 2"; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + void *func = metacall_function("call_test"); + + ASSERT_NE((void *)NULL, (void *)func); + + void *ret = metacallt("call_test", double_ids, 10.0, 2.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); + + metacall_value_destroy(ret); + + /* Call by map using serial */ + ret = metacallfms(func, args_map, sizeof(args_map), allocator); + + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 20); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); metacall_value_destroy(ret); + /* Bad call by map using serial */ + ret = metacallfms(func, args_bad_map, sizeof(args_bad_map), allocator); + + EXPECT_EQ((void *)NULL, (void *)ret); + /* Call by array using serial */ ret = metacallfs(func, args_array, sizeof(args_array), allocator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 20); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)20.0); metacall_value_destroy(ret); + /* Bad call by array using serial */ + ret = metacallfs(func, args_bad_array, sizeof(args_bad_array), allocator); + + EXPECT_EQ((void *)NULL, (void *)ret); + + /* TODO: Implement spread operator */ + /* + static const char buffer[] = + "function spread_parameter(...args) {\n" + " console.log(args);\n" + " return 'ACK: OK!';\n" + "}\n" + "module.exports = {\n" + " spread_parameter,\n" + "};\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + static const char args_map_spread[] = + "{\n" + " \"id\": \"5555555-0000027\",\n" + " \"loanType\": \"Retail\",\n" + " \"lendingCategory\": \"Yeet\",\n" + " \"comments\": {\n" + " \"comment\": \"Rich-Test via Concert/Postman (Auto Approve - Attempt)\"\n" + " }\n" + "}\n"; + + func = metacall_function("spread_parameter"); + + ret = metacallfms(func, args_map_spread, sizeof(args_map_spread), allocator); + + ASSERT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "ACK: OK!"); + + metacall_value_destroy(ret); + */ } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_NODE */ metacall_allocator_destroy(allocator); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_async_multiple_test/CMakeLists.txt b/source/tests/metacall_node_async_multiple_test/CMakeLists.txt new file mode 100644 index 0000000000..77c1e4b31e --- /dev/null +++ b/source/tests/metacall_node_async_multiple_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-async-multiple-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_async_multiple_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_async_multiple_test/source/main.cpp b/source/tests/metacall_node_async_multiple_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_async_multiple_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_async_multiple_test/source/metacall_node_async_multiple_test.cpp b/source/tests/metacall_node_async_multiple_test/source/metacall_node_async_multiple_test.cpp new file mode 100644 index 0000000000..157a31a02c --- /dev/null +++ b/source/tests/metacall_node_async_multiple_test/source/metacall_node_async_multiple_test.cpp @@ -0,0 +1,210 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +std::atomic success_callbacks{}; + +class metacall_node_async_multiple_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_async_multiple_test, DefaultConstructor) +{ + const size_t iteration_size = 1000; + + struct async_context + { + int value; + } ctx = { + 234 + }; + + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const util = require('util');\n" + "const delay = ms => new Promise(resolve => setTimeout(resolve, ms))\n" + "async function f(x) {\n" + "\tawait delay(2000);\n" + "\treturn new Promise(r => console.log(`Promise f executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" + "}\n" + "async function g(x) {\n" + "\tawait delay(2000);\n" + "\treturn new Promise((_, r) => console.log(`Promise g executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" + "}\n" + "async function h() {\n" + "\tawait delay(2000);\n" + "\treturn new Promise((resolve) => resolve(34));\n" + "}\n" + "module.exports = { f, g, h };\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + for (size_t i = 0; i < iteration_size; ++i) + { + void *args[] = { + metacall_value_create_double(10.0) + }; + + /* Test resolve */ + void *future = metacall_await( + "f", args, [](void *result, void *data) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 10.0, (double) metacall_value_to_double(result)); + + EXPECT_NE((void *) NULL, (void *) data); + + struct async_context * ctx = static_cast(data); + + EXPECT_EQ((int) 234, (int) ctx->value); + + printf("Resolve C Callback\n"); + + return metacall_value_create_double(15.0); }, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + printf("Reject C Callback\n"); + + return NULL; }, static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + + /* Test reject */ + future = metacall_await( + "g", args, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + printf("Resolve C Callback\n"); + + return NULL; }, [](void *result, void *data) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 10.0, (double) metacall_value_to_double(result)); + + EXPECT_NE((void *) NULL, (void *) data); + + struct async_context * ctx = static_cast(data); + + EXPECT_EQ((int) 234, (int) ctx->value); + + printf("Reject C Callback\n"); + + ++success_callbacks; + + return metacall_value_create_double(15.0); }, static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + + metacall_value_destroy(args[0]); + + /* Test future */ + future = metacall("h"); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + void *ret = metacall_await_future( + metacall_value_to_future(future), [](void *result, void *) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 34.0, (double) metacall_value_to_double(result)); + + ++success_callbacks; + + return metacall_value_create_double(155.0); }, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + return NULL; }, NULL); + + metacall_value_destroy(future); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + void *last = metacall_await_future( + metacall_value_to_future(ret), [](void *result, void *) -> void * { + EXPECT_NE((void *) NULL, (void *) result); + + EXPECT_EQ((enum metacall_value_id) metacall_value_id(result), (enum metacall_value_id) METACALL_DOUBLE); + + EXPECT_EQ((double) 155.0, (double) metacall_value_to_double(result)); + + ++success_callbacks; + + return NULL; }, [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int) 1, (int) this_should_never_be_executed); + + return NULL; }, NULL); + + metacall_value_destroy(last); + + metacall_value_destroy(ret); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* Total amount of successful callbacks must be 3 */ + EXPECT_EQ((int)success_callbacks, (int)(3 * iteration_size)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ +} diff --git a/source/tests/metacall_node_async_resources_test/CMakeLists.txt b/source/tests/metacall_node_async_resources_test/CMakeLists.txt new file mode 100644 index 0000000000..17e130e414 --- /dev/null +++ b/source/tests/metacall_node_async_resources_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-async-resources-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_async_resources_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_async_resources_test/source/main.cpp b/source/tests/metacall_node_async_resources_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_async_resources_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_async_resources_test/source/metacall_node_async_resources_test.cpp b/source/tests/metacall_node_async_resources_test/source/metacall_node_async_resources_test.cpp new file mode 100644 index 0000000000..82d2178970 --- /dev/null +++ b/source/tests/metacall_node_async_resources_test/source/metacall_node_async_resources_test.cpp @@ -0,0 +1,50 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_event_loop_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_event_loop_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "gram/index.js" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_async_test/CMakeLists.txt b/source/tests/metacall_node_async_test/CMakeLists.txt new file mode 100644 index 0000000000..d58db66234 --- /dev/null +++ b/source/tests/metacall_node_async_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-async-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_async_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_async_test/source/main.cpp b/source/tests/metacall_node_async_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_async_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp b/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp new file mode 100644 index 0000000000..e7a8395355 --- /dev/null +++ b/source/tests/metacall_node_async_test/source/metacall_node_async_test.cpp @@ -0,0 +1,264 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +std::atomic success_callbacks{}; + +class metacall_node_async_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_async_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const util = require('util');\n" + "function f(x) {\n" + "\treturn new Promise(r => console.log(`Promise f executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" + "}\n" + "function g(x) {\n" + "\treturn new Promise((_, r) => console.log(`Promise g executed: ${util.inspect(r)} -> ${x}`) || r(x));\n" + "}\n" + "function h() {\n" + "\treturn new Promise((resolve) => resolve(34));\n" + "}\n" + "async function i() {\n" + "\tthrow Error('Hi there!');\n" + "}\n" + "module.exports = { f, g, h, i };\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + void *args[] = { + metacall_value_create_double(10.0) + }; + + struct async_context + { + int value; + } ctx = { + 234 + }; + + /* Test resolve */ + void *future = metacall_await( + "f", + args, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)10.0, (double)metacall_value_to_double(result)); + + EXPECT_NE((void *)NULL, (void *)data); + + struct async_context *ctx = static_cast(data); + + EXPECT_EQ((int)234, (int)ctx->value); + + printf("Resolve C Callback\n"); + + return metacall_value_create_double(15.0); + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + printf("Reject C Callback\n"); + + return NULL; + }, + static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + + /* Test reject */ + future = metacall_await( + "g", + args, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + printf("Resolve C Callback\n"); + + return NULL; + }, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)10.0, (double)metacall_value_to_double(result)); + + EXPECT_NE((void *)NULL, (void *)data); + + struct async_context *ctx = static_cast(data); + + EXPECT_EQ((int)234, (int)ctx->value); + + printf("Reject C Callback\n"); + + ++success_callbacks; + + return metacall_value_create_double(15.0); + }, + static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + + metacall_value_destroy(args[0]); + + /* Test future */ + future = metacall("h"); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + void *ret = metacall_await_future( + metacall_value_to_future(future), + [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result)); + + ++success_callbacks; + + return metacall_value_create_double(155.0); + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + return NULL; + }, + NULL); + + metacall_value_destroy(future); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + void *last = metacall_await_future( + metacall_value_to_future(ret), + [](void *result, void *) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)155.0, (double)metacall_value_to_double(result)); + + ++success_callbacks; + + return NULL; + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + return NULL; + }, + NULL); + + metacall_value_destroy(last); + + metacall_value_destroy(ret); + + /* Test throw exception inside async function */ + future = metacall_await( + "i", + metacall_null_args, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + printf("Resolve C Callback\n"); + + return NULL; + }, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_EXCEPTION); + + EXPECT_NE((void *)NULL, (void *)data); + + struct async_context *ctx = static_cast(data); + + EXPECT_EQ((int)234, (int)ctx->value); + + printf("Reject C Callback\n"); + + ++success_callbacks; + + return metacall_value_create_double(15.0); + }, + static_cast(&ctx)); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* Total amount of successful callbacks must be 4 */ + EXPECT_EQ((int)success_callbacks, (int)4); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ +} diff --git a/source/tests/metacall_node_await_chain_test/CMakeLists.txt b/source/tests/metacall_node_await_chain_test/CMakeLists.txt new file mode 100644 index 0000000000..52e4ab39e3 --- /dev/null +++ b/source/tests/metacall_node_await_chain_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-await-chain-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_await_chain_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_await_chain_test/source/main.cpp b/source/tests/metacall_node_await_chain_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_await_chain_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_await_chain_test/source/metacall_node_await_chain_test.cpp b/source/tests/metacall_node_await_chain_test/source/metacall_node_await_chain_test.cpp new file mode 100644 index 0000000000..1efdd46827 --- /dev/null +++ b/source/tests/metacall_node_await_chain_test/source/metacall_node_await_chain_test.cpp @@ -0,0 +1,101 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +class metacall_node_await_chain_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_await_chain_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + std::atomic callbacks_executed(0); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = + "async function sleep(ms) { return new Promise(resolve => setTimeout(() => resolve(1), ms)) }\n" + "module.exports = { sleep };\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + /* We will try to emulate this: */ + /* sleep(100).then(v => v * 2).then(v => v + 3); */ + + void *args[] = { + metacall_value_create_double(100) + }; + + auto resolve1 = [](void *result, void *data) -> void * { + std::atomic *callbacks_executed = static_cast *>(data); + double v = metacall_value_to_double(result); + EXPECT_EQ((double)1.0, (double)v); + EXPECT_EQ((unsigned int)0, (unsigned int)*callbacks_executed); + ++(*callbacks_executed); + return metacall_value_create_double(v * 2.0); + }; + + auto resolve2 = [](void *result, void *data) -> void * { + std::atomic *callbacks_executed = static_cast *>(data); + double v = metacall_value_to_double(result); + EXPECT_EQ((double)2.0, (double)v); + EXPECT_EQ((unsigned int)1, (unsigned int)*callbacks_executed); + ++(*callbacks_executed); + return metacall_value_create_double(v + 3.0); + }; + + auto resolve3 = [](void *result, void *data) -> void * { + std::atomic *callbacks_executed = static_cast *>(data); + EXPECT_EQ((double)5.0, (double)metacall_value_to_double(result)); + EXPECT_EQ((unsigned int)2, (unsigned int)*callbacks_executed); + ++(*callbacks_executed); + return NULL; + }; + + void *f1 = metacall_await("sleep", args, resolve1, NULL, static_cast(&callbacks_executed)); + + metacall_value_destroy(args[0]); + + void *f2 = metacall_await_future(metacall_value_to_future(f1), resolve2, NULL, static_cast(&callbacks_executed)); + + void *f3 = metacall_await_future(metacall_value_to_future(f2), resolve3, NULL, static_cast(&callbacks_executed)); + + metacall_value_destroy(f1); + metacall_value_destroy(f2); + metacall_value_destroy(f3); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + + EXPECT_EQ((unsigned int)3, (unsigned int)callbacks_executed); +} diff --git a/source/tests/metacall_node_call_test/CMakeLists.txt b/source/tests/metacall_node_call_test/CMakeLists.txt index a4320abd27..c0a877d4df 100644 --- a/source/tests/metacall_node_call_test/CMakeLists.txt +++ b/source/tests/metacall_node_call_test/CMakeLists.txt @@ -85,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_call_test/source/main.cpp b/source/tests/metacall_node_call_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_node_call_test/source/main.cpp +++ b/source/tests/metacall_node_call_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp b/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp index 4572c56a5a..7117e09419 100644 --- a/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp +++ b/source/tests/metacall_node_call_test/source/metacall_node_call_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include #define METACALL_CALL_TEST_SIZE 10000 @@ -35,37 +35,37 @@ TEST_F(metacall_node_call_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + metacall_log_null(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; const size_t call_size = METACALL_CALL_TEST_SIZE; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); for (size_t iterator = 0; iterator < call_size; ++iterator) { - void * ret = metacallt("call_test", hello_boy_double_ids, 3.0, 4.0); + void *ret = metacallt("call_test", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 12.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)12.0); metacall_value_destroy(ret); } } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_callback_test/CMakeLists.txt b/source/tests/metacall_node_callback_test/CMakeLists.txt new file mode 100644 index 0000000000..1ba50b6375 --- /dev/null +++ b/source/tests/metacall_node_callback_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# +# Executable name and options +# + +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE + OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# Target name +set(target metacall-node-callback-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_callback_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + PY_PORT_LIBRARY_PATH=${CMAKE_SOURCE_DIR}/source/ports/py_port +) diff --git a/source/tests/metacall_node_callback_test/source/main.cpp b/source/tests/metacall_node_callback_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_callback_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_callback_test/source/metacall_node_callback_test.cpp b/source/tests/metacall_node_callback_test/source/metacall_node_callback_test.cpp new file mode 100644 index 0000000000..a49f61313b --- /dev/null +++ b/source/tests/metacall_node_callback_test/source/metacall_node_callback_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_callback_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_callback_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "host.js" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + ret = metacall("a"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + void *handle = metacall_handle("node", "host.js"); + + EXPECT_NE((void *)NULL, (void *)handle); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_clear_mem_test/CMakeLists.txt b/source/tests/metacall_node_clear_mem_test/CMakeLists.txt new file mode 100644 index 0000000000..d7f9e9124c --- /dev/null +++ b/source/tests/metacall_node_clear_mem_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-clear-mem-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_clear_mem_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_clear_mem_test/source/main.cpp b/source/tests/metacall_node_clear_mem_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_clear_mem_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_clear_mem_test/source/metacall_node_clear_mem_test.cpp b/source/tests/metacall_node_clear_mem_test/source/metacall_node_clear_mem_test.cpp new file mode 100644 index 0000000000..ae63885773 --- /dev/null +++ b/source/tests/metacall_node_clear_mem_test/source/metacall_node_clear_mem_test.cpp @@ -0,0 +1,68 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_clear_mem_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_clear_mem_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_null(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char bufferA[] = + "console.log('A');\n"; + + void *handleA = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", bufferA, sizeof(bufferA), &handleA)); + + EXPECT_NE((void *)NULL, (void *)handleA); + + EXPECT_EQ((int)0, (int)metacall_clear(handleA)); + + const char bufferB[] = + "console.log('B');\n"; + + void *handleB = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", bufferB, sizeof(bufferB), &handleB)); + + EXPECT_NE((void *)NULL, (void *)handleB); + + EXPECT_EQ((int)0, (int)metacall_clear(handleB)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_default_export_test/CMakeLists.txt b/source/tests/metacall_node_default_export_test/CMakeLists.txt new file mode 100644 index 0000000000..c009589339 --- /dev/null +++ b/source/tests/metacall_node_default_export_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-default-export-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_default_export_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_default_export_test/source/main.cpp b/source/tests/metacall_node_default_export_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_default_export_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp b/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp new file mode 100644 index 0000000000..890c8b8726 --- /dev/null +++ b/source/tests/metacall_node_default_export_test/source/metacall_node_default_export_test.cpp @@ -0,0 +1,87 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_default_export_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_default_export_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js", "export.js" + }; + + const enum metacall_value_id double_id[] = { + METACALL_DOUBLE + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + /* When using module exports functions will be exported explicitly */ + EXPECT_EQ((void *)NULL, (void *)metacall_function("this_function_should_not_be_exported")); + + /* Test default export */ + ret = metacallt("export_this_function_even_without_module_exports", double_id, 3.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_event_loop_signal_test/CMakeLists.txt b/source/tests/metacall_node_event_loop_signal_test/CMakeLists.txt new file mode 100644 index 0000000000..cb3e4701ce --- /dev/null +++ b/source/tests/metacall_node_event_loop_signal_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-event-loop-signal-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_event_loop_signal_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_event_loop_signal_test/source/main.cpp b/source/tests/metacall_node_event_loop_signal_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_event_loop_signal_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_event_loop_signal_test/source/metacall_node_event_loop_signal_test.cpp b/source/tests/metacall_node_event_loop_signal_test/source/metacall_node_event_loop_signal_test.cpp new file mode 100644 index 0000000000..54d7cb8fa2 --- /dev/null +++ b/source/tests/metacall_node_event_loop_signal_test/source/metacall_node_event_loop_signal_test.cpp @@ -0,0 +1,58 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_event_loop_signal_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_event_loop_signal_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_null(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + // This test verifies that signals do not prevent event loop from exiting + const char buffer[] = + "process.on('SIGINT', () => { console.log('SIGINT') });\n"; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), &handle)); + + EXPECT_NE((void *)NULL, (void *)handle); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_event_loop_test/CMakeLists.txt b/source/tests/metacall_node_event_loop_test/CMakeLists.txt new file mode 100644 index 0000000000..d1d83d4416 --- /dev/null +++ b/source/tests/metacall_node_event_loop_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-event-loop-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_event_loop_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_event_loop_test/source/main.cpp b/source/tests/metacall_node_event_loop_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_event_loop_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp b/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp new file mode 100644 index 0000000000..306cda37b0 --- /dev/null +++ b/source/tests/metacall_node_event_loop_test/source/metacall_node_event_loop_test.cpp @@ -0,0 +1,50 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_event_loop_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_event_loop_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "server.js" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_exception_test/CMakeLists.txt b/source/tests/metacall_node_exception_test/CMakeLists.txt new file mode 100644 index 0000000000..1129b31459 --- /dev/null +++ b/source/tests/metacall_node_exception_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-exception-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_exception_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_exception_test/source/main.cpp b/source/tests/metacall_node_exception_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_exception_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_exception_test/source/metacall_node_exception_test.cpp b/source/tests/metacall_node_exception_test/source/metacall_node_exception_test.cpp new file mode 100644 index 0000000000..fd1c3b2e26 --- /dev/null +++ b/source/tests/metacall_node_exception_test/source/metacall_node_exception_test.cpp @@ -0,0 +1,79 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_exception_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_exception_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = + "module.exports = {\n" + " js_return_error: () => new Error('Yeet'),\n" + " js_throw_error: () => { throw new Error('YeetThrown') },\n" + " js_throw_value: () => { throw 56 },\n" + "};\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("js_return_error"); + + struct metacall_exception_type ex; + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("Yeet", ex.message); + + metacall_value_destroy(ret); + + ret = metacall("js_throw_error"); + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("YeetThrown", ex.message); + + metacall_value_destroy(ret); + + ret = metacall("js_throw_value"); + + void *number = metacall_throwable_value(metacall_value_to_throwable(ret)); + + EXPECT_EQ((double)56.0, (double)metacall_value_to_double(number)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_extension_test/CMakeLists.txt b/source/tests/metacall_node_extension_test/CMakeLists.txt new file mode 100644 index 0000000000..f56da937ab --- /dev/null +++ b/source/tests/metacall_node_extension_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-extension-test) +message(STATUS "Test ${target}") + +# Add node extension test +add_subdirectory(node_extension_test) + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_extension_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + METACALL_NODE_EXTENSION_PATH="${PROJECT_OUTPUT_DIR}/node_extension_test.node" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + node_extension_test +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_extension_test/node_extension_test/CMakeLists.txt b/source/tests/metacall_node_extension_test/node_extension_test/CMakeLists.txt new file mode 100644 index 0000000000..1efbefcc03 --- /dev/null +++ b/source/tests/metacall_node_extension_test/node_extension_test/CMakeLists.txt @@ -0,0 +1,186 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# External dependencies +# + +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(SEND_ERROR "NodeJS libraries not found") + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target node_extension_test) + +# Exit here if required dependencies are not met +message(STATUS "Script ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/node_extension_test.c +) + +if(WIN32 AND MSVC_VERSION GREATER_EQUAL 1200) + set(sources + ${sources} + ${source_path}/node_extension_test_win32_delay_load.cpp + ) +endif() + +# Group source files +set(source_group "Source Files") +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create library +# + +# Build library +add_library(${target} MODULE + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" + BUNDLE $<$:$<$>> + + # Set NodeJS extension properies + SUFFIX ".node" + PREFIX "" + DEBUG_POSTFIX "" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + + ${NodeJS_INCLUDE_DIRS} # NodeJS includes + + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${NodeJS_LIBRARY} # NodeJS library + $<$:delayimp.lib> # Delayed library + + PUBLIC + ${DEFAULT_LIBRARIES} + + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + $<$:NODEJS_LIBRARY_NAME="${NodeJS_LIBRARY_NAME}"> + $<$>:_LARGEFILE_SOURCE> + $<$>:_FILE_OFFSET_BITS=64> + $<$,$>:_DARWIN_USE_64_BIT_INODE=1> + + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + $<$:/IGNORE:4199> + $<$:/DELAYLOAD:${NodeJS_LIBRARY_NAME}> + $<$,$>:-undefined dynamic_lookup> + + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + + INTERFACE +) diff --git a/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test.c b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test.c new file mode 100644 index 0000000000..a0df94beac --- /dev/null +++ b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test.c @@ -0,0 +1,53 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +static napi_value Method(napi_env env, napi_callback_info info) +{ + napi_status status; + napi_value world; + (void)info; + status = napi_create_string_utf8(env, "world", 5, &world); + if (status != napi_ok) + { + return NULL; + } + return world; +} + +#define DECLARE_NAPI_METHOD(name, func) \ + { \ + name, 0, func, 0, 0, 0, napi_default, 0 \ + } + +static napi_value Init(napi_env env, napi_value exports) +{ + napi_status status; + napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method); + status = napi_define_properties(env, exports, 1, &desc); + if (status != napi_ok) + { + return NULL; + } + return exports; +} + +NAPI_MODULE(node_extension_test, Init) diff --git a/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test_win32_delay_load.cpp b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test_win32_delay_load.cpp new file mode 100644 index 0000000000..fc968f846a --- /dev/null +++ b/source/tests/metacall_node_extension_test/node_extension_test/source/node_extension_test_win32_delay_load.cpp @@ -0,0 +1,54 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma managed(push, off) + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include +#include + +static const char node_library_name[] = NODEJS_LIBRARY_NAME; + +static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo *info) +{ + HMODULE m; + + if (event != dliNotePreLoadLibrary) + { + return NULL; + } + + if (_stricmp(info->szDll, node_library_name) != 0) + { + return NULL; + } + + m = GetModuleHandle(NULL); + return (FARPROC)m; +} + +decltype(__pfnDliNotifyHook2) __pfnDliNotifyHook2 = load_exe_hook; + +#pragma managed(pop) diff --git a/source/tests/metacall_node_extension_test/source/main.cpp b/source/tests/metacall_node_extension_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_extension_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_extension_test/source/metacall_node_extension_test.cpp b/source/tests/metacall_node_extension_test/source/metacall_node_extension_test.cpp new file mode 100644 index 0000000000..ef5fd6caf7 --- /dev/null +++ b/source/tests/metacall_node_extension_test/source/metacall_node_extension_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +class metacall_node_extension_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_extension_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + METACALL_NODE_EXTENSION_PATH + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + ret = metacallv("hello", metacall_null_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ("world", metacall_value_to_string(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_fail_env_var_test/CMakeLists.txt b/source/tests/metacall_node_fail_env_var_test/CMakeLists.txt new file mode 100644 index 0000000000..ecd42051ef --- /dev/null +++ b/source/tests/metacall_node_fail_env_var_test/CMakeLists.txt @@ -0,0 +1,162 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-fail-env-var-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_fail_env_var_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +# Do not define LOADER_LIBRARY_PATH, which is needed by node loader +# in order to test if the node loader fails gracefully +set(NODE_FAIL_ENVIRONMENT_VARIABLES ${TESTS_ENVIRONMENT_VARIABLES}) + +list(REMOVE_ITEM NODE_FAIL_ENVIRONMENT_VARIABLES "LOADER_LIBRARY_PATH=${LOADER_LIBRARY_PATH}") + +test_environment_variables(${target} + "" + ${NODE_FAIL_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_fail_env_var_test/source/main.cpp b/source/tests/metacall_node_fail_env_var_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_fail_env_var_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_fail_env_var_test/source/metacall_node_fail_env_var_test.cpp b/source/tests/metacall_node_fail_env_var_test/source/metacall_node_fail_env_var_test.cpp new file mode 100644 index 0000000000..b6ef3dc8a2 --- /dev/null +++ b/source/tests/metacall_node_fail_env_var_test/source/metacall_node_fail_env_var_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_fail_env_var_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_fail_env_var_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "this_does_not_exists_yeet.js" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_fail_load_leak_test/CMakeLists.txt b/source/tests/metacall_node_fail_load_leak_test/CMakeLists.txt new file mode 100644 index 0000000000..3b1cc5bc5e --- /dev/null +++ b/source/tests/metacall_node_fail_load_leak_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-fail-load-leak-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_fail_load_leak_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_fail_load_leak_test/source/main.cpp b/source/tests/metacall_node_fail_load_leak_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_fail_load_leak_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_fail_load_leak_test/source/metacall_node_fail_load_leak_test.cpp b/source/tests/metacall_node_fail_load_leak_test/source/metacall_node_fail_load_leak_test.cpp new file mode 100644 index 0000000000..a47ec3c226 --- /dev/null +++ b/source/tests/metacall_node_fail_load_leak_test/source/metacall_node_fail_load_leak_test.cpp @@ -0,0 +1,74 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const { metacall_load_from_memory } = require('metacall');\n" + "metacall_load_from_memory('node', 'throw new Error(\"fail\")');\n"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("hey"); + + EXPECT_EQ((void *)NULL, (void *)ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_fail_test/CMakeLists.txt b/source/tests/metacall_node_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..2f46c9261f --- /dev/null +++ b/source/tests/metacall_node_fail_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_fail_test/source/main.cpp b/source/tests/metacall_node_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_fail_test/source/metacall_node_fail_test.cpp b/source/tests/metacall_node_fail_test/source/metacall_node_fail_test.cpp new file mode 100644 index 0000000000..e052ed25b2 --- /dev/null +++ b/source/tests/metacall_node_fail_test/source/metacall_node_fail_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "this_does_not_exists_yeet.js" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + const char buffer[] = + "function pure ()random 453 code lol()\n"; + + EXPECT_EQ((int)1, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_inline_test/CMakeLists.txt b/source/tests/metacall_node_inline_test/CMakeLists.txt index 812e4d6b4f..8f7c771f26 100644 --- a/source/tests/metacall_node_inline_test/CMakeLists.txt +++ b/source/tests/metacall_node_inline_test/CMakeLists.txt @@ -85,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_inline_test/source/main.cpp b/source/tests/metacall_node_inline_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_node_inline_test/source/main.cpp +++ b/source/tests/metacall_node_inline_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp b/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp index 9d8bd81e97..1c578f6eab 100644 --- a/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp +++ b/source/tests/metacall_node_inline_test/source/metacall_node_inline_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_node_inline_test : public testing::Test { @@ -33,34 +33,32 @@ TEST_F(metacall_node_inline_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "inline.js" }; - const enum metacall_value_id inline_double_id[] = - { + const enum metacall_value_id inline_double_id[] = { METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); ret = metacallt("inline", inline_double_id, 3.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 6.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { @@ -68,13 +66,13 @@ TEST_F(metacall_node_inline_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -83,5 +81,5 @@ TEST_F(metacall_node_inline_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt b/source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt new file mode 100644 index 0000000000..e09b978263 --- /dev/null +++ b/source/tests/metacall_node_multithread_deadlock_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-multithread-deadlock-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_multithread_deadlock_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_multithread_deadlock_test/source/main.cpp b/source/tests/metacall_node_multithread_deadlock_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_multithread_deadlock_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_multithread_deadlock_test/source/metacall_node_multithread_deadlock_test.cpp b/source/tests/metacall_node_multithread_deadlock_test/source/metacall_node_multithread_deadlock_test.cpp new file mode 100644 index 0000000000..39ff07dabe --- /dev/null +++ b/source/tests/metacall_node_multithread_deadlock_test/source/metacall_node_multithread_deadlock_test.cpp @@ -0,0 +1,136 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include + +std::atomic success_callbacks{}; +static const int call_size = 200000; +static const int thread_size = 8; + +class metacall_node_multithread_deadlock_test : public testing::Test +{ +public: +}; + +void test_await(void) +{ + for (int i = 0; i < call_size; ++i) + { + void *future = metacall_await( + "f", + metacall_null_args, + [](void *result, void *data) -> void * { + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result)); + + EXPECT_EQ((void *)NULL, (void *)data); + + ++success_callbacks; + + return metacall_value_create_double(15.0); + }, + [](void *, void *) -> void * { + int this_should_never_be_executed = 0; + + EXPECT_EQ((int)1, (int)this_should_never_be_executed); + + return NULL; + }, + NULL); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } +} + +void test_call(void) +{ + for (int i = 0; i < call_size; ++i) + { + void *result = metacallv_s("g", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_DOUBLE); + + EXPECT_EQ((double)34.0, (double)metacall_value_to_double(result)); + + metacall_value_destroy(result); + + ++success_callbacks; + } +} + +TEST_F(metacall_node_multithread_deadlock_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "async function f() {\n" + "\treturn 34;\n" + "}\n" + "function g() {\n" + "\treturn 34;\n" + "}\n" + "module.exports = { f, g };\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + std::thread threads[thread_size]; + + for (int i = 0; i < thread_size; ++i) + { + threads[i] = std::thread(test_call); + } + + for (int i = 0; i < thread_size; ++i) + { + threads[i].join(); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + EXPECT_EQ((int)success_callbacks, (int)(call_size * thread_size)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ +} diff --git a/source/tests/metacall_node_native_code_test/CMakeLists.txt b/source/tests/metacall_node_native_code_test/CMakeLists.txt new file mode 100644 index 0000000000..2f485938eb --- /dev/null +++ b/source/tests/metacall_node_native_code_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-native-code-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_native_code_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + node_port +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_native_code_test/source/main.cpp b/source/tests/metacall_node_native_code_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_native_code_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_native_code_test/source/metacall_node_native_code_test.cpp b/source/tests/metacall_node_native_code_test/source/metacall_node_native_code_test.cpp new file mode 100644 index 0000000000..4366808cc2 --- /dev/null +++ b/source/tests/metacall_node_native_code_test/source/metacall_node_native_code_test.cpp @@ -0,0 +1,72 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_node_unsupported_features_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_unsupported_features_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer[] = + "const { metacall_inspect } = process._linkedBinding('node_loader_port_module');\n" + "module.exports = { metacall_inspect }\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("metacall_inspect")); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_await_test/CMakeLists.txt b/source/tests/metacall_node_port_await_test/CMakeLists.txt new file mode 100644 index 0000000000..819a878a01 --- /dev/null +++ b/source/tests/metacall_node_port_await_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + node_port +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_port_await_test/source/main.cpp b/source/tests/metacall_node_port_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_await_test/source/metacall_node_port_await_test.cpp b/source/tests/metacall_node_port_await_test/source/metacall_node_port_await_test.cpp new file mode 100644 index 0000000000..43cb8e8db0 --- /dev/null +++ b/source/tests/metacall_node_port_await_test/source/metacall_node_port_await_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_port_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall_await, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('node', `" + /* NodeJS */ + "function sleep(n) { return new Promise(resolve => { setTimeout(() => resolve(n), 200); }); }\n" + "module.exports = { sleep };\n" + "`);\n" + /* NodeJS Check */ + "let buffer = new SharedArrayBuffer(4);\n" + "let int32 = new Int32Array(buffer);\n" + "Atomics.store(int32, 0, 0);\n" + "process.on('exit', () => {\n" + " if (Atomics.load(int32, 0) != 1) {\n" + " process.exit(3);\n" + " }\n" + "});\n" + /* NodeJS Promise */ + "metacall_await('sleep', 32).then(v => {\n" + " console.log('RESULT:', v);\n" + " if (v !== 32) {\n" + " process.exit(1);\n" + " }\n" + " Atomics.add(int32, 0, 1);\n" + "}).catch(v => {\n" + " console.log('ERROR:', v);\n" + " process.exit(2);\n" + "});\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt b/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt new file mode 100644 index 0000000000..ab55d51e7d --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/CMakeLists.txt @@ -0,0 +1,176 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# External dependencies +# + +find_package(LibGit2) + +if(NOT LibGit2_FOUND) + message(WARNING "LibGit2 libraries not found, skipping test metacall-node-port-c-lib-test") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-c-lib-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_c_lib_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # LibGit2 paths + LIBGIT2_LIBRARY_DIR="${LibGit2_LIBRARY_DIR}" + LIBGIT2_INCLUDE_DIR="${LibGit2_INCLUDE_DIR}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_port + node_loader + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_port_c_lib_test/source/main.cpp b/source/tests/metacall_node_port_c_lib_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp b/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp new file mode 100644 index 0000000000..7070bbf985 --- /dev/null +++ b/source/tests/metacall_node_port_c_lib_test/source/metacall_node_port_c_lib_test.cpp @@ -0,0 +1,57 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_port_c_lib_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_c_lib_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = + /* NodeJS */ + "const assert = require('assert');\n" + "const { metacall_execution_path, metacall_load_from_package_export } = require('" METACALL_NODE_PORT_PATH "');\n" + /* C Lib Paths */ + "metacall_execution_path('c', '" LIBGIT2_INCLUDE_DIR "');\n" + "metacall_execution_path('c', '" LIBGIT2_LIBRARY_DIR "');\n" + /* C Lib Require */ + "const git2 = metacall_load_from_package_export('c', 'git2');\n" + "const { git_libgit2_init, git_libgit2_shutdown } = git2;\n" + "console.log(git2);\n" + /* C Lib Assert */ + "assert(git_libgit2_init() >= 0, 'libgit2 initialization failed');\n" + "git_libgit2_shutdown();\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_rs_test/CMakeLists.txt b/source/tests/metacall_node_port_rs_test/CMakeLists.txt new file mode 100644 index 0000000000..57422785bd --- /dev/null +++ b/source/tests/metacall_node_port_rs_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-rs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_rs_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_port + node_loader + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_port_rs_test/source/main.cpp b/source/tests/metacall_node_port_rs_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_rs_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_rs_test/source/metacall_node_port_rs_test.cpp b/source/tests/metacall_node_port_rs_test/source/metacall_node_port_rs_test.cpp new file mode 100644 index 0000000000..dc54f4d0c9 --- /dev/null +++ b/source/tests/metacall_node_port_rs_test/source/metacall_node_port_rs_test.cpp @@ -0,0 +1,59 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_port_rs_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_rs_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Rust */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_RS) + { + static const char buffer[] = + /* NodeJS */ + "const assert = require('assert');\n" + "require('" METACALL_NODE_PORT_PATH "');\n" + /* Rust Require */ + "const { new_string, add_vec2, add_float, return_vec } = require('./basic.rs');\n" + /* Rust Assert */ + "assert.strictEqual(new_string(123), 'get number 123');\n" + "assert.strictEqual(add_vec2([1, 2, 3, 4]), 10);\n" + "assert.strictEqual(add_float(12, 23), 35);\n" + "assert.strictEqual(return_vec().reduce((partialSum, a) => partialSum + a, 0), 15);\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_RS */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_port_test/CMakeLists.txt b/source/tests/metacall_node_port_test/CMakeLists.txt new file mode 100644 index 0000000000..5568059519 --- /dev/null +++ b/source/tests/metacall_node_port_test/CMakeLists.txt @@ -0,0 +1,244 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_MOCK OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_TS OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port Test path + METACALL_NODE_PORT_TEST_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/test.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13603) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #9 node_loader_port_load_from_file_export(napi_env__*, napi_callback_info__*) /usr/local/metacall/source/loaders/node_loader/source/node_loader_port.cpp:395 (libnode_loaderd.so+0x113c5) + # #10 (libnode.so.72+0x7b6344) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +# Enable cobol test if it is built +if(OPTION_BUILD_LOADERS_COB) + set(COBOL_DEPENDENCY cob_loader) + set(TESTS_ENVIRONMENT_VARIABLES_COB "OPTION_BUILD_LOADERS_COB=1") +endif() + +# Enable c test if it is built +if(OPTION_BUILD_LOADERS_C) + set(C_DEPENDENCY c_loader) + set(TESTS_ENVIRONMENT_VARIABLES_C "OPTION_BUILD_LOADERS_C=1") +endif() + +# Enable rust test if it is built +if(OPTION_BUILD_LOADERS_RS) + set(RS_DEPENDENCY rs_loader) + set(TESTS_ENVIRONMENT_VARIABLES_RS "OPTION_BUILD_LOADERS_RS=1") +endif() + +# Add dependencies and optional dependencies +add_dependencies(${target} + node_port + node_loader + mock_loader + py_loader + rb_loader + ts_loader + ${COBOL_DEPENDENCY} + ${C_DEPENDENCY} + ${RS_DEPENDENCY} +) + +# Disable OpenSSL related tests if versions are incompatible +set(NodeJS_EXECUTABLE_ONLY ON) + +find_package(NodeJS) +find_package(Python3 COMPONENTS Interpreter) + +if(NodeJS_FOUND AND Python3_Interpreter_FOUND) + execute_process( + COMMAND ${NodeJS_EXECUTABLE} -e "console.log(process.versions.openssl)" + OUTPUT_VARIABLE NODEJS_OPENSSL_VERSION + ) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import ssl; print(ssl.OPENSSL_VERSION.split()[1])" + OUTPUT_VARIABLE PYTHON_OPENSSL_VERSION + ) + + if(NOT "${NODEJS_OPENSSL_VERSION}" STREQUAL "" AND NOT "${PYTHON_OPENSSL_VERSION}" STREQUAL "") + string(REGEX MATCHALL "-.*$|[0-9]+" NODEJS_OPENSSL_PARTIAL_VERSION_LIST "${NODEJS_OPENSSL_VERSION}") + list(GET NODEJS_OPENSSL_PARTIAL_VERSION_LIST 0 NODEJS_OPENSSL_VERSION_MAJOR) + list(GET NODEJS_OPENSSL_PARTIAL_VERSION_LIST 1 NODEJS_OPENSSL_VERSION_MINOR) + list(GET NODEJS_OPENSSL_PARTIAL_VERSION_LIST 2 NODEJS_OPENSSL_VERSION_PATCH) + + string(REGEX MATCHALL "-.*$|[0-9]+" PYTHON_OPENSSL_PARTIAL_VERSION_LIST "${PYTHON_OPENSSL_VERSION}") + list(GET PYTHON_OPENSSL_PARTIAL_VERSION_LIST 0 PYTHON_OPENSSL_VERSION_MAJOR) + list(GET PYTHON_OPENSSL_PARTIAL_VERSION_LIST 1 PYTHON_OPENSSL_VERSION_MINOR) + list(GET PYTHON_OPENSSL_PARTIAL_VERSION_LIST 2 PYTHON_OPENSSL_VERSION_PATCH) + + # If major and minor version match, then enable the OpenSSL related tests (https://github.com/metacall/core/issues/31#issuecomment-1736039845) + if(NODEJS_OPENSSL_VERSION_MAJOR VERSION_EQUAL PYTHON_OPENSSL_VERSION_MAJOR + AND NODEJS_OPENSSL_VERSION_MINOR VERSION_EQUAL PYTHON_OPENSSL_VERSION_MINOR) + set(TESTS_ENVIRONMENT_VARIABLES_OPENSSL "OPTION_NODEJS_PYTHON_OPENSSL_MATCH=1") + endif() + endif() +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${TESTS_ENVIRONMENT_VARIABLES_COB} + ${TESTS_ENVIRONMENT_VARIABLES_C} + ${TESTS_ENVIRONMENT_VARIABLES_RS} + ${TESTS_ENVIRONMENT_VARIABLES_OPENSSL} + "NODE_PORT_TEST_EXPORTS=1" +) diff --git a/source/tests/metacall_node_port_test/source/main.cpp b/source/tests/metacall_node_port_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_port_test/source/metacall_node_port_test.cpp b/source/tests/metacall_node_port_test/source/metacall_node_port_test.cpp new file mode 100644 index 0000000000..ab1d6df112 --- /dev/null +++ b/source/tests/metacall_node_port_test/source/metacall_node_port_test.cpp @@ -0,0 +1,89 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include + +class metacall_node_port_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_port_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + METACALL_NODE_PORT_TEST_PATH + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + + struct await_data_type + { + std::mutex m; + std::condition_variable c; + } await_data; + + std::unique_lock lock(await_data.m); + + auto accept = [](void *v, void *data) -> void * { + struct await_data_type *await_data = static_cast(data); + std::unique_lock lock(await_data->m); + const char *str = metacall_value_to_string(v); + EXPECT_STREQ(str, "Tests passed without errors"); + await_data->c.notify_one(); + return NULL; + }; + + auto reject = [](void *, void *data) -> void * { + static const int promise_rejected = 0; + struct await_data_type *await_data = static_cast(data); + std::unique_lock lock(await_data->m); + EXPECT_EQ((int)1, (int)promise_rejected); // This should never happen + await_data->c.notify_one(); + return NULL; + }; + + void *future = metacall_await("main", metacall_null_args, accept, reject, static_cast(&await_data)); + + ASSERT_NE((void *)NULL, (void *)future); + + ASSERT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + await_data.c.wait(lock); + + metacall_value_destroy(future); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_async_after_destroy_test/CMakeLists.txt b/source/tests/metacall_node_python_async_after_destroy_test/CMakeLists.txt new file mode 100644 index 0000000000..6c88f28d6b --- /dev/null +++ b/source/tests/metacall_node_python_async_after_destroy_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-async-after-destroy-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_async_after_destroy_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_async_after_destroy_test/source/main.cpp b/source/tests/metacall_node_python_async_after_destroy_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_async_after_destroy_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_async_after_destroy_test/source/metacall_node_python_async_after_destroy_test.cpp b/source/tests/metacall_node_python_async_after_destroy_test/source/metacall_node_python_async_after_destroy_test.cpp new file mode 100644 index 0000000000..71ebbcdeab --- /dev/null +++ b/source/tests/metacall_node_python_async_after_destroy_test/source/metacall_node_python_async_after_destroy_test.cpp @@ -0,0 +1,69 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_async_after_destroy_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_async_after_destroy_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python & Mock */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `" + /* Python */ + "def sum(a, b):\n" + " return a + b\n" + "`);\n" + "function log(x) { console.log(x); return x; }\n" + "setTimeout(() => { log(metacall('sum', 3, 4)) === 7 || process.exit(1) }, 2000);\n" + "setTimeout(() => { log(metacall_inspect()) || process.exit(1) }, 4000);\n" + "setTimeout(() => { log(metacall('sum', 3, 4)) === 7 || process.exit(1) }, 6000);\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + /* This should be called before the setTimeout ends, so we can test if the node loader waits + * to be destroyed until the event loop is completely cleaned, so we can call safely to python + * even if the destroy was already emmited, for more info: + * https://github.com/metacall/core/commit/4b61c2f3b22065472828dc7b718defbfc4ac3884 + * https://github.com/metacall/core/commit/e963515cf68e04c91ba0612227d5ef586c08aab6 + * https://github.com/metacall/core/commit/df701d779af0d2a7cb1f27e33aacf5f17ae20f4b + * https://github.com/metacall/core/commit/1fc9c9244d7a5553861a458813d2cf1488ebe08c + * https://github.com/metacall/core/commit/9b64ee533079fa0d543fc346fb7149d1086451f0 + * https://github.com/metacall/core/commit/22bd999c281f23aac04cea7df435a836631706da + */ + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_await_extended_test/CMakeLists.txt b/source/tests/metacall_node_python_await_extended_test/CMakeLists.txt new file mode 100644 index 0000000000..a82165e896 --- /dev/null +++ b/source/tests/metacall_node_python_await_extended_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-await-extended-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_await_extended_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_await_extended_test/source/main.cpp b/source/tests/metacall_node_python_await_extended_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_await_extended_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_await_extended_test/source/metacall_node_python_await_extended_test.cpp b/source/tests/metacall_node_python_await_extended_test/source/metacall_node_python_await_extended_test.cpp new file mode 100644 index 0000000000..d773f0ba76 --- /dev/null +++ b/source/tests/metacall_node_python_await_extended_test/source/metacall_node_python_await_extended_test.cpp @@ -0,0 +1,94 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_await_extended_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_await_extended_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall_await, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "const util = require('util')\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "import threading\n" + "counter = 0\n" + "async def python_simple(n):\n" + "\tglobal counter\n" + "\tprint('inside python_simple', threading.current_thread().ident, counter, ':', n)\n" + "\tcounter = counter + 1\n" + "\tsys.stdout.flush()\n" + "\treturn n\n" + // "import asyncio\n" + // "async def python_simple(n):\n" + // " await asyncio.sleep(1)\n" + // " return n\n" + "`);\n" + /* Debug */ + "console.log('--------------------------------------------------------------------')\n" + "console.log(util.inspect(metacall_inspect(), false, null, true))\n" + "console.log('--------------------------------------------------------------------')\n" + /* NodeJS Check */ + "const size = 10000;\n" + "let buffer = new SharedArrayBuffer(4);\n" + "let int32 = new Int32Array(buffer);\n" + "Atomics.store(int32, 0, 0);\n" + "process.on('exit', () => {\n" + " if (Atomics.load(int32, 0) != size) {\n" + " process.exit(3);\n" + " }\n" + "});\n" + /* NodeJS Promise */ + "for (let i = 0; i < size; i++) {\n" + " metacall_await('python_simple', 32).then(v => {\n" + " console.log('RESULT:', v, Atomics.load(int32, 0));\n" + " if (v !== 32) {\n" + " process.exit(1);\n" + " }\n" + " Atomics.add(int32, 0, 1);\n" + " }).catch(v => {\n" + " console.log('ERROR:', v);\n" + " process.exit(2);\n" + " });\n" + "}\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_await_test/CMakeLists.txt b/source/tests/metacall_node_python_await_test/CMakeLists.txt new file mode 100644 index 0000000000..3553dbc2ca --- /dev/null +++ b/source/tests/metacall_node_python_await_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PY_DEBUG_ENVIRONMENT_VARIABLES + PYTHONTHREADDEBUG=1 + PYTHONASYNCIODEBUG=1 + ) +else() + set(PY_DEBUG_ENVIRONMENT_VARIABLES) +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_await_test/source/main.cpp b/source/tests/metacall_node_python_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_await_test/source/metacall_node_python_await_test.cpp b/source/tests/metacall_node_python_await_test/source/metacall_node_python_await_test.cpp new file mode 100644 index 0000000000..9e63b1aa3c --- /dev/null +++ b/source/tests/metacall_node_python_await_test/source/metacall_node_python_await_test.cpp @@ -0,0 +1,88 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall_await, metacall_load_from_memory, metacall_inspect } = require('" METACALL_NODE_PORT_PATH "');\n" + "const util = require('util')\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "import threading\n" + "async def python_simple(n):\n" + "\tprint('inside python_simple', threading.current_thread().ident, ':', n)\n" + "\tsys.stdout.flush()\n" + "\treturn n\n" + // "import asyncio\n" + // "async def python_simple(n):\n" + // " await asyncio.sleep(1)\n" + // " return n\n" + "`);\n" + /* Debug */ + "console.log('--------------------------------------------------------------------')\n" + "console.log(util.inspect(metacall_inspect(), false, null, true))\n" + "console.log('--------------------------------------------------------------------')\n" + /* NodeJS Check */ + "let buffer = new SharedArrayBuffer(4);\n" + "let int32 = new Int32Array(buffer);\n" + "Atomics.store(int32, 0, 0);\n" + "process.on('exit', () => {\n" + " if (Atomics.load(int32, 0) != 1) {\n" + " process.exit(3);\n" + " }\n" + "});\n" + /* NodeJS Promise */ + "metacall_await('python_simple', 32).then(v => {\n" + " console.log('RESULT:', v);\n" + " if (v !== 32) {\n" + " process.exit(1);\n" + " }\n" + " Atomics.add(int32, 0, 1);\n" + "}).catch(v => {\n" + " console.log('ERROR:', v);\n" + " process.exit(2);\n" + "});\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_deadlock_test/CMakeLists.txt b/source/tests/metacall_node_python_deadlock_test/CMakeLists.txt new file mode 100644 index 0000000000..55bcbfed39 --- /dev/null +++ b/source/tests/metacall_node_python_deadlock_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-deadlock-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_deadlock_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_tests_properties(${target} PROPERTIES + LABELS ${target} + TIMEOUT 120 # Stop test after 120 seconds (to avoid deadlocking forever) +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_deadlock_test/source/main.cpp b/source/tests/metacall_node_python_deadlock_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_deadlock_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_deadlock_test/source/metacall_node_python_deadlock_test.cpp b/source/tests/metacall_node_python_deadlock_test/source/metacall_node_python_deadlock_test.cpp new file mode 100644 index 0000000000..d5cca59f22 --- /dev/null +++ b/source/tests/metacall_node_python_deadlock_test/source/metacall_node_python_deadlock_test.cpp @@ -0,0 +1,58 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_deadlock_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_deadlock_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer_py[] = "print('asd')\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer_py, sizeof(buffer_py), NULL)); + + static const char buffer_node[] = + "const { metacall_load_from_memory, metacall } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `\n" + "def something():\n" + " pass\n" + "`);\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer_node, sizeof(buffer_node), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_exception_test/CMakeLists.txt b/source/tests/metacall_node_python_exception_test/CMakeLists.txt new file mode 100644 index 0000000000..0d4b368692 --- /dev/null +++ b/source/tests/metacall_node_python_exception_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-exception-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_exception_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_exception_test/source/main.cpp b/source/tests/metacall_node_python_exception_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_exception_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_exception_test/source/metacall_node_python_exception_test.cpp b/source/tests/metacall_node_python_exception_test/source/metacall_node_python_exception_test.cpp new file mode 100644 index 0000000000..73f59386c1 --- /dev/null +++ b/source/tests/metacall_node_python_exception_test/source/metacall_node_python_exception_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_exception_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_exception_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "const { metacall_load_from_memory, metacall } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `\n" + "def py_throw_error():\n" + " raise TypeError('yeet')\n" + "`);\n" + "try {\n" + " metacall('py_throw_error');\n" + " process.exit(1);\n" + "} catch (e) {\n" + " console.log('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&');\n" + " console.log(e);\n" + " console.log('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&');\n" + " if (e.message !== 'yeet' || e.code !== 'TypeError') process.exit(1);\n" + "}\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_port_mock_test/CMakeLists.txt b/source/tests/metacall_node_python_port_mock_test/CMakeLists.txt new file mode 100644 index 0000000000..cbe593374d --- /dev/null +++ b/source/tests/metacall_node_python_port_mock_test/CMakeLists.txt @@ -0,0 +1,164 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_MOCK OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-port-mock-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_port_mock_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader + mock_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_port_mock_test/source/main.cpp b/source/tests/metacall_node_python_port_mock_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_port_mock_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_port_mock_test/source/metacall_node_python_port_mock_test.cpp b/source/tests/metacall_node_python_port_mock_test/source/metacall_node_python_port_mock_test.cpp new file mode 100644 index 0000000000..b1c9b76b4c --- /dev/null +++ b/source/tests/metacall_node_python_port_mock_test/source/metacall_node_python_port_mock_test.cpp @@ -0,0 +1,68 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_port_mock_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_port_mock_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python & Mock */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) && defined(OPTION_BUILD_LOADERS_MOCK) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall, metacall_load_from_memory } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + /* Mock */ + "from asd.mock import two_doubles\n" + "print('........................................................')\n" + "print(two_doubles(3.0, 6.0))\n" // This works + "print('........................................................')\n" + "def py_func(cb):\n" + " return cb(two_doubles)\n" + "print(py_func(lambda f: f(3, 4)))\n" // This works too + "`);\n" + // This does not, probably the error is when converting mock_func from metacall value to napi: + "const result = metacall('py_func', (mock_func) => mock_func(3, 4));\n" + "console.log('Result:', result);\n" + "if (result !== 3.1416) process.exit(1);\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY && OPTION_BUILD_LOADERS_MOCK */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_port_ruby_test/CMakeLists.txt b/source/tests/metacall_node_python_port_ruby_test/CMakeLists.txt new file mode 100644 index 0000000000..6dfbd6c337 --- /dev/null +++ b/source/tests/metacall_node_python_port_ruby_test/CMakeLists.txt @@ -0,0 +1,164 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-port-ruby-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_port_ruby_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_port_ruby_test/source/main.cpp b/source/tests/metacall_node_python_port_ruby_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_port_ruby_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_port_ruby_test/source/metacall_node_python_port_ruby_test.cpp b/source/tests/metacall_node_python_port_ruby_test/source/metacall_node_python_port_ruby_test.cpp new file mode 100644 index 0000000000..19acb40e98 --- /dev/null +++ b/source/tests/metacall_node_python_port_ruby_test/source/metacall_node_python_port_ruby_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_python_port_ruby_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_python_port_ruby_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python & Ruby */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) && defined(OPTION_BUILD_LOADERS_RB) + { + static const char buffer[] = + /* NodeJS */ + "const { metacall, metacall_load_from_memory } = require('" METACALL_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('py', `" + /* Python */ + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + /* Ruby: */ + "from hello.rb import say_sum_ducktyped, say_multiply_ducktyped\n" + "def py_func(js_func):\n" + " return js_func(lambda x, y: say_multiply_ducktyped(x, y) - say_sum_ducktyped(x, y))\n" + "`);\n" + "const result = metacall('py_func', (py_lambda) => py_lambda(3, 4));\n" + "console.log('Result:', result);\n" + "if (result !== 5.0) process.exit(1);\n"; // (3 * 4) - (3 + 4) = 5 + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY && OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_python_ruby_test/CMakeLists.txt b/source/tests/metacall_node_python_ruby_test/CMakeLists.txt new file mode 100644 index 0000000000..b5e2914333 --- /dev/null +++ b/source/tests/metacall_node_python_ruby_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB) + return() +endif() + +# The test uses node fetch so it requires a version >= 18 +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(SEND_ERROR "NodeJS libraries not found") + return() +endif() + +if(NodeJS_VERSION VERSION_LESS "18.0.0") + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-python-ruby-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_python_ruby_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_python_ruby_test/source/main.cpp b/source/tests/metacall_node_python_ruby_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_python_ruby_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_python_ruby_test/source/metacall_node_python_ruby_test.cpp b/source/tests/metacall_node_python_ruby_test/source/metacall_node_python_ruby_test.cpp new file mode 100644 index 0000000000..6253dffc4c --- /dev/null +++ b/source/tests/metacall_node_python_ruby_test/source/metacall_node_python_ruby_test.cpp @@ -0,0 +1,114 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +std::atomic success_callbacks{}; + +class metacall_node_python_ruby_test : public testing::Test +{ +public: +}; + +static void *hello_world_await_ok(void *result, void *data) +{ + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((void *)NULL, (void *)data); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_BOOL); + + EXPECT_EQ((boolean)metacall_value_to_bool(result), (boolean)1L); + + ++success_callbacks; + + return NULL; +} + +static void *hello_world_await_fail(void *, void *data) +{ + int this_should_never_happen = 1; + + EXPECT_EQ((void *)NULL, (void *)data); + + EXPECT_NE((int)0, (int)this_should_never_happen); + + return NULL; +} + +TEST_F(metacall_node_python_ruby_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Python */ + { + static const char buffer[] = + "print('Hello Python')\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } + + /* Ruby */ + { + static const char buffer[] = + "puts 'Hello Ruby'\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer, sizeof(buffer), NULL)); + } + + /* NodeJS */ + { + static const char buffer[] = + "module.exports = {\n" + " test: async function () {\n" + " try {\n" + " const result = fetch('http://github.com/service/https://www.google.com/', { signal: AbortSignal.timeout(60000) });\n" + " console.log(result);\n" + " return true;\n" + " } catch (e) {\n" + " console.log(e);\n" + " return false\n" + " }\n" + " }\n" + "};\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + /* Await function that returns */ + void *ret = metacall_await("test", metacall_null_args, hello_world_await_ok, hello_world_await_fail, NULL); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(ret); + } + + EXPECT_EQ((int)success_callbacks, (int)1); + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_reentrant_test/CMakeLists.txt b/source/tests/metacall_node_reentrant_test/CMakeLists.txt new file mode 100644 index 0000000000..6dfff474f4 --- /dev/null +++ b/source/tests/metacall_node_reentrant_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-reentrant-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_reentrant_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_reentrant_test/source/main.cpp b/source/tests/metacall_node_reentrant_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_reentrant_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp b/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp new file mode 100644 index 0000000000..09f0c7af6c --- /dev/null +++ b/source/tests/metacall_node_reentrant_test/source/metacall_node_reentrant_test.cpp @@ -0,0 +1,123 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#ifndef METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH + #error "The path to the NodeJS port is not defined" +#endif + +class metacall_node_reentrant_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_reentrant_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + /* List of loads (1 level) */ + { + static const char buffer[] = + "const { metacall_load_from_memory, metacall } = require('" METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory0: () => 0 };');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory1: () => 1 };');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory2: () => 2 };');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory3: () => 3 };');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory4: () => 4 };');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory5: () => 5 };');\n"; + + static const char tag[] = "node"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + } + + /* Reentrant */ + { + static const char buffer[] = + "const { metacall_load_from_memory, metacall } = require('" METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH "');\n" + "metacall_load_from_memory('node', 'module.exports = { node_memory_reentrant: () => 4 };');\n" + "console.log('Reentrant node_memory_reentrant result (main):', metacall('node_memory_reentrant'));\n" + "metacall_load_from_memory('node', '" + "const { metacall } = require(\"" METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH "\");" + "console.log(\"Reentrant node_memory_reentrant result (reentrant):\", metacall(\"node_memory_reentrant\"));" + "');\n"; + + static const char tag[] = "node"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + } + + /* Recursion */ + { + static const char buffer[] = + "const { metacall } = require('" METACALL_NODE_REENTRANT_TEST_NODE_PORT_PATH "');\n" + "const log = (v) => { console.log(v); return v };" + "module.exports = { node_memory_recursive: (v) => (v > 0 && v < 100000000000) ? metacall('node_memory_recursive', log(v * 2)) : log(v) };\n"; + + static const char tag[] = "node"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + + const enum metacall_value_id node_memory_recursive_ids[] = { + METACALL_INT + }; + + void *ret = metacallt("node_memory_recursive", node_memory_recursive_ids, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)137438953472.0, (double)metacall_value_to_double(ret)); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_node_signal_handler_test/CMakeLists.txt b/source/tests/metacall_node_signal_handler_test/CMakeLists.txt new file mode 100644 index 0000000000..473ad45e51 --- /dev/null +++ b/source/tests/metacall_node_signal_handler_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_NODE) + return() +endif() + +include(Portability) + +if(NOT PROJECT_OS_FAMILY STREQUAL unix) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-signal-handler-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_signal_handler_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # NodeJS Port path + METACALL_NODE_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/node_port/index.js" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_signal_handler_test/source/main.cpp b/source/tests/metacall_node_signal_handler_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_signal_handler_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_signal_handler_test/source/metacall_node_signal_handler_test.cpp b/source/tests/metacall_node_signal_handler_test/source/metacall_node_signal_handler_test.cpp new file mode 100644 index 0000000000..61e1ef0754 --- /dev/null +++ b/source/tests/metacall_node_signal_handler_test/source/metacall_node_signal_handler_test.cpp @@ -0,0 +1,134 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +std::atomic_bool callback_result(false); +std::atomic_bool signal_result(false); + +class metacall_node_signal_handler_test : public testing::Test +{ +public: +}; + +void *c_callback(size_t, void *[], void *) +{ + callback_result.store(true); + return metacall_value_create_long(raise(SIGUSR2)); /* Propagate signal */ +} + +static void handle_signals(int s) +{ + switch (s) + { + case SIGUSR1: + fprintf(stdout, "c++: received signal SIGUSR1\n"); + break; + case SIGUSR2: + fprintf(stdout, "c++: received signal SIGUSR1\n"); + signal_result.store(true); + break; + case SIGINT: + fprintf(stdout, "c++: received signal SIGINT\n"); + break; + case SIGCONT: + fprintf(stdout, "c++: received signal SIGCONT\n"); + break; + case SIGCHLD: + fprintf(stdout, "c++: received signal SIGCHLD\n"); + break; + case SIGTSTP: + fprintf(stdout, "c++: received signal SIGTSTP\n"); + break; + case SIGSTOP: + fprintf(stdout, "c++: received signal SIGSTOP\n"); + break; + default: + fprintf(stdout, "c++: received signal number %d\n", s); + break; + } +} + +TEST_F(metacall_node_signal_handler_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Register signal */ + signal(SIGUSR2, handle_signals); + + ASSERT_EQ((int)0, (int)metacall_register("c_callback", c_callback, NULL, METACALL_LONG, 0)); + + const char buffer[] = + "const { metacall } = require('" METACALL_NODE_PORT_PATH "');\n" + "let notified = false;\n" + "const notify = () => {\n" + " if (notified === true) return;\n" + " notified = true;\n" + " metacall('c_callback');\n" + "};\n" + "const cp = require('child_process');\n" + "let sp = cp.spawn('ps');\n" + "sp.stdout.on('data', data => {\n" + " console.log('node: stdout: ' + data.toString());\n" + "});\n" + "process.on('SIGCHLD', () => {\n" + " console.log(`node: Received SIGCHLD signal in process`);\n" + " notify()\n" + "});\n" + "sp.on('exit', code => {\n" + " console.log(`node: child process exited with code ${code}`);\n" + " notify();\n" /* Sometimes exit event gets received before SIGCHLD, so we notify anyway to c_callback */ + "});\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); + + EXPECT_EQ((bool)callback_result.load(), (bool)true); + EXPECT_EQ((bool)signal_result.load(), (bool)true); +} diff --git a/source/tests/metacall_node_test/CMakeLists.txt b/source/tests/metacall_node_test/CMakeLists.txt index 04bd256139..7c08d7c101 100644 --- a/source/tests/metacall_node_test/CMakeLists.txt +++ b/source/tests/metacall_node_test/CMakeLists.txt @@ -85,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -108,6 +95,8 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE ${DEFAULT_COMPILE_DEFINITIONS} + # For execution path test + METACALL_NODE_TEST_EXECUTION_PATH="${CMAKE_SOURCE_DIR}/source/scripts/node/inline/source" ) # @@ -119,11 +108,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,12 +134,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + node_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_node_test/source/main.cpp b/source/tests/metacall_node_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_node_test/source/main.cpp +++ b/source/tests/metacall_node_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_node_test/source/metacall_node_test.cpp b/source/tests/metacall_node_test/source/metacall_node_test.cpp index 955f6245ae..f38954e090 100644 --- a/source/tests/metacall_node_test/source/metacall_node_test.cpp +++ b/source/tests/metacall_node_test/source/metacall_node_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include #include -#include #include +#include class metacall_node_test : public testing::Test { @@ -33,34 +33,94 @@ TEST_F(metacall_node_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); + + metacall_value_destroy(ret); + + // Test execution path + EXPECT_EQ((int)0, (int)metacall_execution_path("node", METACALL_NODE_TEST_EXECUTION_PATH)); + + const char *node_execution_path_scripts[] = { + "inline.js" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_execution_path_scripts, sizeof(node_execution_path_scripts) / sizeof(node_execution_path_scripts[0]), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("inline")); + + // Test load from memory + map type + const char buffer[] = { + "module.exports = { test_map: (m) => m.a + m.b };" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function("test_map")); + + void *args[] = { + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla0 = metacall_value_to_array(map_value[0]); + + static const char key0[] = "a"; + + tupla0[0] = metacall_value_create_string(key0, sizeof(key0) - 1); + tupla0[1] = metacall_value_create_double(5.0); + + map_value[1] = metacall_value_create_array(NULL, 2); + + void **tupla1 = metacall_value_to_array(map_value[1]); + + static const char key1[] = "b"; - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + tupla1[0] = metacall_value_create_string(key1, sizeof(key1) - 1); + tupla1[1] = metacall_value_create_double(7.0); + + ret = metacallv_s("test_map", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((double)12.0, (double)metacall_value_to_double(ret)); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + + const char *node_module_scripts[] = { + "path" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_module_scripts, sizeof(node_module_scripts) / sizeof(node_module_scripts[0]), &handle)); + + EXPECT_NE((void *)NULL, (void *)handle); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_NODE */ /* Print inspect information */ { @@ -68,18 +128,20 @@ TEST_F(metacall_node_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); - char * inspect_str = metacall_inspect(&size, allocator); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_GT((size_t)size, (size_t)0); - EXPECT_GT((size_t) size, (size_t) 0); + std::cout << inspect_str << std::endl; metacall_allocator_free(allocator, inspect_str); metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_node_typescript_test/CMakeLists.txt b/source/tests/metacall_node_typescript_test/CMakeLists.txt new file mode 100644 index 0000000000..499baca66e --- /dev/null +++ b/source/tests/metacall_node_typescript_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-typescript-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_typescript_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_node_typescript_test/source/main.cpp b/source/tests/metacall_node_typescript_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_node_typescript_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_node_typescript_test/source/metacall_node_typescript_test.cpp b/source/tests/metacall_node_typescript_test/source/metacall_node_typescript_test.cpp new file mode 100644 index 0000000000..8bf39ee566 --- /dev/null +++ b/source/tests/metacall_node_typescript_test/source/metacall_node_typescript_test.cpp @@ -0,0 +1,142 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_typescript_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_typescript_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + void *ret = NULL; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + ret = metacall("typed_sum", 3.0, 4.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); + + metacall_value_destroy(ret); + + /* Test arrays */ + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_double(3.0); + array_value[1] = metacall_value_create_double(5.0); + array_value[2] = metacall_value_create_double(7.0); + + ret = metacallv("typed_array", array_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(array_args[0]); + + /* Test records */ + void *record_args[] = { + metacall_value_create_map(NULL, 1) + }; + + void **map_value = metacall_value_to_map(record_args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla = metacall_value_to_array(map_value[0]); + + static const char key[] = "element"; + + tupla[0] = metacall_value_create_string(key, sizeof(key) - 1); + tupla[1] = metacall_value_create_double(6.0); + + ret = metacallv("object_record", record_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(record_args[0]); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_destroy_order_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_destroy_order_test/CMakeLists.txt new file mode 100644 index 0000000000..0bf7f39890 --- /dev/null +++ b/source/tests/metacall_plugin_extension_destroy_order_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_CLI OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-destroy-order-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_destroy_order_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +set(METACALL_PLUGIN_PATH "${PROJECT_OUTPUT_DIR}/metacall_plugin_extension_destroy_order_test") + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Plugin path + METACALL_PLUGIN_PATH="${METACALL_PLUGIN_PATH}" +) + +# Isolate the plugin into a different directory in order to make it fully reproducible +add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${METACALL_PLUGIN_PATH}/cli_core_plugin" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_OUTPUT_DIR}/plugins/cli/repl/cli_core_plugin" "${METACALL_PLUGIN_PATH}/cli_core_plugin" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + plugin_extension + cli_core_plugin # Requires cli_core_plugin (from CLI) for reproducing it +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_destroy_order_test/source/main.cpp b/source/tests/metacall_plugin_extension_destroy_order_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_destroy_order_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_destroy_order_test/source/metacall_plugin_extension_destroy_order_test.cpp b/source/tests/metacall_plugin_extension_destroy_order_test/source/metacall_plugin_extension_destroy_order_test.cpp new file mode 100644 index 0000000000..7d094870ca --- /dev/null +++ b/source/tests/metacall_plugin_extension_destroy_order_test/source/metacall_plugin_extension_destroy_order_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_destroy_order_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_destroy_order_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&handle) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_invalid_path_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_invalid_path_test/CMakeLists.txt new file mode 100644 index 0000000000..6e5da526f6 --- /dev/null +++ b/source/tests/metacall_plugin_extension_invalid_path_test/CMakeLists.txt @@ -0,0 +1,163 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_CLI OR NOT TARGET cli_core_plugin) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-invalid-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_invalid_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +set(METACALL_PLUGIN_PATH "${PROJECT_OUTPUT_DIR}/this/folder/does/not/exist") + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Plugin path + METACALL_PLUGIN_PATH="${METACALL_PLUGIN_PATH}" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + plugin_extension + cli_core_plugin # Requires cli_core_plugin (from CLI) for reproducing it +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_invalid_path_test/source/main.cpp b/source/tests/metacall_plugin_extension_invalid_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_invalid_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_invalid_path_test/source/metacall_plugin_extension_invalid_path_test.cpp b/source/tests/metacall_plugin_extension_invalid_path_test/source/metacall_plugin_extension_invalid_path_test.cpp new file mode 100644 index 0000000000..31fec9d9b4 --- /dev/null +++ b/source/tests/metacall_plugin_extension_invalid_path_test/source/metacall_plugin_extension_invalid_path_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_extension_invalid_path_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_extension_invalid_path_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&handle) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_local_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_local_test/CMakeLists.txt new file mode 100644 index 0000000000..174628c0ec --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-local-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_local_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + METACALL_PLUGIN_PATH="${CMAKE_CURRENT_SOURCE_DIR}/plugins" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + py_loader + plugin_extension +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/.gitignore b/source/tests/metacall_plugin_extension_local_test/plugins/.gitignore new file mode 100644 index 0000000000..68bc17f9ff --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/metacall.json b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/metacall.json new file mode 100644 index 0000000000..ec2ac1db88 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginA.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/pluginA.py b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/pluginA.py new file mode 100644 index 0000000000..afb07fc875 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginA/pluginA.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginA(): + print('Hello World from extensionA!!') + return 6 diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/metacall-extB.json b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/metacall-extB.json new file mode 100644 index 0000000000..8fab42668b --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/metacall-extB.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginB.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/pluginB.py b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/pluginB.py new file mode 100644 index 0000000000..18a4c47424 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginB/pluginB.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginB(): + print('Hello World from extensionB!!') + return 7 diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/metacall-extC.json b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/metacall-extC.json new file mode 100644 index 0000000000..bd690f9322 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/metacall-extC.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "pluginC.js" + ] +} diff --git a/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/pluginC.js b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/pluginC.js new file mode 100644 index 0000000000..a069ef53bc --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/plugins/test_pluginC/pluginC.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node + +function pluginC() { + console.log('Hello World, from extensionC'); + return 8; +} + +module.exports = { + pluginC +}; diff --git a/source/tests/metacall_plugin_extension_local_test/source/main.cpp b/source/tests/metacall_plugin_extension_local_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_local_test/source/metacall_plugin_extension_local_test.cpp b/source/tests/metacall_plugin_extension_local_test/source/metacall_plugin_extension_local_test.cpp new file mode 100644 index 0000000000..060200f35d --- /dev/null +++ b/source/tests/metacall_plugin_extension_local_test/source/metacall_plugin_extension_local_test.cpp @@ -0,0 +1,123 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_extension_local_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_extension_local_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1), + metacall_value_create_ptr(&handle) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(result); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + void *ret = metacallhv_s(handle, "pluginA", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)6, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } + + { + void *ret = metacallhv_s(handle, "pluginB", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)7, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + void *ret = metacallhv_s(handle, "pluginC", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((double)8.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_plugin_extension_test/CMakeLists.txt b/source/tests/metacall_plugin_extension_test/CMakeLists.txt new file mode 100644 index 0000000000..ed506009f8 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-plugin-extension-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_plugin_extension_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + METACALL_PLUGIN_PATH="${CMAKE_CURRENT_SOURCE_DIR}/plugins" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + node_loader + py_loader + plugin_extension +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_plugin_extension_test/plugins/.gitignore b/source/tests/metacall_plugin_extension_test/plugins/.gitignore new file mode 100644 index 0000000000..68bc17f9ff --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/metacall.json b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/metacall.json new file mode 100644 index 0000000000..ec2ac1db88 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginA.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/pluginA.py b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/pluginA.py new file mode 100644 index 0000000000..afb07fc875 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginA/pluginA.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginA(): + print('Hello World from extensionA!!') + return 6 diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/metacall-extB.json b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/metacall-extB.json new file mode 100644 index 0000000000..8fab42668b --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/metacall-extB.json @@ -0,0 +1,7 @@ +{ + "language_id": "py", + "path": ".", + "scripts": [ + "pluginB.py" + ] +} diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/pluginB.py b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/pluginB.py new file mode 100644 index 0000000000..18a4c47424 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginB/pluginB.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +def pluginB(): + print('Hello World from extensionB!!') + return 7 diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/metacall-extC.json b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/metacall-extC.json new file mode 100644 index 0000000000..bd690f9322 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/metacall-extC.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "pluginC.js" + ] +} diff --git a/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/pluginC.js b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/pluginC.js new file mode 100644 index 0000000000..a069ef53bc --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/plugins/test_pluginC/pluginC.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node + +function pluginC() { + console.log('Hello World, from extensionC'); + return 8; +} + +module.exports = { + pluginC +}; diff --git a/source/tests/metacall_plugin_extension_test/source/main.cpp b/source/tests/metacall_plugin_extension_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_plugin_extension_test/source/metacall_plugin_extension_test.cpp b/source/tests/metacall_plugin_extension_test/source/metacall_plugin_extension_test.cpp new file mode 100644 index 0000000000..4d1d4ac99a --- /dev/null +++ b/source/tests/metacall_plugin_extension_test/source/metacall_plugin_extension_test.cpp @@ -0,0 +1,121 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_plugin_extension_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_plugin_extension_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Extension */ + void *handle = metacall_plugin_extension(); + + ASSERT_NE((void *)NULL, (void *)handle); + + void *args[] = { + metacall_value_create_string(METACALL_PLUGIN_PATH, sizeof(METACALL_PLUGIN_PATH) - 1) + }; + + void *result = metacallhv_s(handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + ASSERT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(result)); + + metacall_value_destroy(args[0]); + metacall_value_destroy(result); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + void *ret = metacall("pluginA"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)6, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } + + { + void *ret = metacall("pluginB"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)7, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + void *ret = metacall("pluginC"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((double)8.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_async_test/CMakeLists.txt b/source/tests/metacall_python_async_test/CMakeLists.txt new file mode 100644 index 0000000000..c329c7a319 --- /dev/null +++ b/source/tests/metacall_python_async_test/CMakeLists.txt @@ -0,0 +1,152 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-async-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_async_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_async_test/source/main.cpp b/source/tests/metacall_python_async_test/source/main.cpp new file mode 100644 index 0000000000..33a4612744 --- /dev/null +++ b/source/tests/metacall_python_async_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_async_test/source/metacall_python_async_test.cpp b/source/tests/metacall_python_async_test/source/metacall_python_async_test.cpp new file mode 100644 index 0000000000..736753c3d9 --- /dev/null +++ b/source/tests/metacall_python_async_test/source/metacall_python_async_test.cpp @@ -0,0 +1,164 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2020 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +class metacall_python_async_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_async_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + /* TODO: Create another test using Curio library? */ + const char buffer[] = + "import asyncio\n" + "import threading\n" + "import sys\n" + + "print('sync message', threading.current_thread().ident)\n" + "sys.stdout.flush()\n" + + "async def my_async_fn(n):\n" + "\tprint('inside my sleep async', threading.current_thread().ident, ':', n)\n" + "\tsys.stdout.flush()\n" + "\treturn 58\n" + + "async def my_async_fail_fn(n):\n" + "\tprint('inside my sleep fail async', threading.current_thread().ident, ':', n)\n" + "\tsys.stdout.flush()\n" + "\traise Exception(15)\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + /* Test for asyncness Python introspection */ + EXPECT_EQ((int)1, metacall_function_async(metacall_function("my_async_fn"))); + + void *args[] = { + metacall_value_create_long(2L) + }; + + std::thread::id this_id = std::this_thread::get_id(); + std::cout << "thread " << this_id << std::endl; + + /* Test resolve */ + { + auto resolve = [](void *result, void *) -> void * { + std::thread::id this_id = std::this_thread::get_id(); + std::cout << "thread " << this_id << std::endl; + + printf("Got into C callback at least\n"); + fflush(stdout); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_LONG); + + EXPECT_EQ((long)metacall_value_to_long(result), (long)58L); + + printf("Resolve C Callback\n"); + fflush(stdout); + + return NULL; + }; + + auto reject = [](void *, void *) -> void * { + /* If we reach here, there's a serious bug in the C code that is called by python after the task is done */ + int never_executed = 0; + EXPECT_EQ((int)1, (int)never_executed); + + printf("Reject C Callback\n"); + fflush(stdout); + + return NULL; + }; + + void *future = metacall_await("my_async_fn", args, resolve, reject, NULL); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } + + /* Test reject */ + { + auto resolve = [](void *, void *) -> void * { + /* If we reach here, there's a serious bug in the C code that is called by python after the task is done */ + int never_executed = 0; + EXPECT_EQ((int)1, (int)never_executed); + + printf("Reject C Callback\n"); + fflush(stdout); + + return NULL; + }; + + auto reject = [](void *result, void *) -> void * { + std::thread::id this_id = std::this_thread::get_id(); + std::cout << "thread " << this_id << std::endl; + + printf("Got into C callback at least\n"); + fflush(stdout); + + EXPECT_NE((void *)NULL, (void *)result); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(result), (enum metacall_value_id)METACALL_LONG); + + EXPECT_EQ((long)metacall_value_to_long(result), (long)15L); + + printf("Resolve C Callback\n"); + fflush(stdout); + + return NULL; + }; + + void *future = metacall_await("my_async_fail_fn", args, resolve, reject, NULL); + + EXPECT_NE((void *)NULL, (void *)future); + + EXPECT_EQ((enum metacall_value_id)metacall_value_id(future), (enum metacall_value_id)METACALL_FUTURE); + + metacall_value_destroy(future); + } + + metacall_value_destroy(args[0]); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_await_test/CMakeLists.txt b/source/tests/metacall_python_await_test/CMakeLists.txt new file mode 100644 index 0000000000..9c34033915 --- /dev/null +++ b/source/tests/metacall_python_await_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PY_DEBUG_ENVIRONMENT_VARIABLES + PYTHONTHREADDEBUG=1 + PYTHONASYNCIODEBUG=1 + ) +else() + set(PY_DEBUG_ENVIRONMENT_VARIABLES) +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_await_test/source/main.cpp b/source/tests/metacall_python_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_await_test/source/metacall_python_await_test.cpp b/source/tests/metacall_python_await_test/source/metacall_python_await_test.cpp new file mode 100644 index 0000000000..71ff8b6f50 --- /dev/null +++ b/source/tests/metacall_python_await_test/source/metacall_python_await_test.cpp @@ -0,0 +1,72 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "import asyncio\n" + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "from metacall import metacall_load_from_memory, metacall_await\n" + "script = \"\"\"\n" + "async def yeet(n):\n" + " return n\n" + "\"\"\"\n" + "result = 0\n" + "lock = threading.Lock()\n" + "metacall_load_from_memory('py', script)\n" + "async def test():\n" + " result = await metacall_await('yeet', 1234)\n" + " if result != 1234:\n" + " sys.exit(1)\n" + " else:\n" + " lock.acquire()\n" + " result = 1\n" + " lock.release()\n" + "asyncio.run(test())\n" + "lock.acquire()\n" + "if result != 1:\n" + " sys.exit(2)\n" + "lock.release()\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_builtins_test/CMakeLists.txt b/source/tests/metacall_python_builtins_test/CMakeLists.txt new file mode 100644 index 0000000000..698df7f4bb --- /dev/null +++ b/source/tests/metacall_python_builtins_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-builtins-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_builtins_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_builtins_test/source/main.cpp b/source/tests/metacall_python_builtins_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_builtins_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_builtins_test/source/metacall_python_builtins_test.cpp b/source/tests/metacall_python_builtins_test/source/metacall_python_builtins_test.cpp new file mode 100644 index 0000000000..a806d04427 --- /dev/null +++ b/source/tests/metacall_python_builtins_test/source/metacall_python_builtins_test.cpp @@ -0,0 +1,81 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_builtins_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_builtins_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "binascii", + "decimal" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_ref[] = { + "sys" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts_ref, sizeof(py_scripts_ref) / sizeof(py_scripts_ref[0]), &handle)); + + EXPECT_EQ((int)0, metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_callback_test/CMakeLists.txt b/source/tests/metacall_python_callback_test/CMakeLists.txt new file mode 100644 index 0000000000..e576930fb8 --- /dev/null +++ b/source/tests/metacall_python_callback_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY + OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-callback-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_callback_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_callback_test/source/main.cpp b/source/tests/metacall_python_callback_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_callback_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_callback_test/source/metacall_python_callback_test.cpp b/source/tests/metacall_python_callback_test/source/metacall_python_callback_test.cpp new file mode 100644 index 0000000000..fa15992d94 --- /dev/null +++ b/source/tests/metacall_python_callback_test/source/metacall_python_callback_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_callback_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_callback_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "host.py" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + ret = metacall("a"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.0); + + metacall_value_destroy(ret); + + void *handle = metacall_handle("py", "host.py"); + + EXPECT_NE((void *)NULL, (void *)handle); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_class_test/CMakeLists.txt b/source/tests/metacall_python_class_test/CMakeLists.txt deleted file mode 100644 index 301d3758fb..0000000000 --- a/source/tests/metacall_python_class_test/CMakeLists.txt +++ /dev/null @@ -1,152 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target metacall-python-class-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/metacall_python_class_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::metacall -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/metacall_python_class_test/source/main.cpp b/source/tests/metacall_python_class_test/source/main.cpp deleted file mode 100644 index 0cbc70d36b..0000000000 --- a/source/tests/metacall_python_class_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp b/source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp deleted file mode 100644 index 4922230235..0000000000 --- a/source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -class metacall_python_class_test : public testing::Test -{ -public: -}; - -TEST_F(metacall_python_class_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - { - const char * py_scripts[] = - { - "classname.py" - }; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - } - #endif /* OPTION_BUILD_LOADERS_PY */ - - /* Print inspect information */ - { - size_t size = 0; - - struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - - char * inspect_str = metacall_inspect(&size, allocator); - - EXPECT_NE((char *) NULL, (char *) inspect_str); - - EXPECT_GT((size_t) size, (size_t) 0); - - std::cout << inspect_str << std::endl; - - metacall_allocator_free(allocator, inspect_str); - - metacall_allocator_destroy(allocator); - } - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/metacall_python_dict_test/CMakeLists.txt b/source/tests/metacall_python_dict_test/CMakeLists.txt new file mode 100644 index 0000000000..835a24fed9 --- /dev/null +++ b/source/tests/metacall_python_dict_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-dict-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_dict_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_dict_test/source/main.cpp b/source/tests/metacall_python_dict_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_dict_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp b/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp new file mode 100644 index 0000000000..f5db7671ce --- /dev/null +++ b/source/tests/metacall_python_dict_test/source/metacall_python_dict_test.cpp @@ -0,0 +1,173 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_dict_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_dict_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "dicty.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + void *ret = metacall("nice_dict"); + + EXPECT_NE((void *)NULL, (void *)ret); + + void **dict = metacall_value_to_map(ret); + size_t size = metacall_value_count(ret); + + for (size_t iterator = 0; iterator < size; ++iterator) + { + void **array = metacall_value_to_array(dict[iterator]); + char *key = metacall_value_to_string(array[0]); + + if (strcmp(key, "asd") == 0) + { + EXPECT_EQ((long)123, (long)metacall_value_to_long(array[1])); + } + else if (strcmp(key, "hello") == 0) + { + EXPECT_STREQ(metacall_value_to_string(array[1]), "world"); + } + else if (strcmp(key, "efg") == 0) + { + EXPECT_EQ((double)3.4, (double)metacall_value_to_double(array[1])); + } + else + { + EXPECT_NE((int)0, (int)0); + } + } + + metacall_value_destroy(ret); + + ret = metacall("non_supported_dict"); + + EXPECT_NE((void *)NULL, (void *)ret); + + dict = metacall_value_to_map(ret); + size = metacall_value_count(ret); + + for (size_t iterator = 0; iterator < size; ++iterator) + { + void **array = metacall_value_to_array(dict[iterator]); + char *key = metacall_value_to_string(array[0]); + + if (strcmp(key, "asd") == 0) + { + EXPECT_EQ((long)123, (long)metacall_value_to_long(array[1])); + } + else + { + EXPECT_NE((int)0, (int)0); + } + } + + metacall_value_destroy(ret); + + void *args[] = { + // "old" -> 5 + // "whatever" -> 7 + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla0 = metacall_value_to_array(map_value[0]); + + static const char key0[] = "old"; + + tupla0[0] = metacall_value_create_string(key0, sizeof(key0) - 1); + tupla0[1] = metacall_value_create_long(5); + + map_value[1] = metacall_value_create_array(NULL, 2); + + void **tupla1 = metacall_value_to_array(map_value[1]); + + static const char key1[] = "whatever"; + + tupla1[0] = metacall_value_create_string(key1, sizeof(key1) - 1); + tupla1[1] = metacall_value_create_long(7); + + ret = metacallv_s("with_love_for_pragma_devs", args, 1); + + metacall_value_destroy(args[0]); + + void **ret_map = metacall_value_to_map(ret); + void **ret_pair0 = metacall_value_to_array(ret_map[0]); + char *ret_key0 = metacall_value_to_string(ret_pair0[0]); + long ret_value0 = metacall_value_to_long(ret_pair0[1]); + + EXPECT_STREQ(ret_key0, "new"); + EXPECT_EQ((long)5, (long)ret_value0); + + void **ret_pair1 = metacall_value_to_array(ret_map[1]); + char *ret_key1 = metacall_value_to_string(ret_pair1[0]); + long ret_value1 = metacall_value_to_long(ret_pair1[1]); + + EXPECT_STREQ(ret_key1, "whatever"); + EXPECT_EQ((long)7, (long)ret_value1); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_exception_test/CMakeLists.txt b/source/tests/metacall_python_exception_test/CMakeLists.txt new file mode 100644 index 0000000000..e06790523f --- /dev/null +++ b/source/tests/metacall_python_exception_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-exception-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_exception_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_exception_test/source/main.cpp b/source/tests/metacall_python_exception_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_exception_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_exception_test/source/metacall_python_exception_test.cpp b/source/tests/metacall_python_exception_test/source/metacall_python_exception_test.cpp new file mode 100644 index 0000000000..14ec35d757 --- /dev/null +++ b/source/tests/metacall_python_exception_test/source/metacall_python_exception_test.cpp @@ -0,0 +1,76 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_exception_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_exception_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "def py_throw_error():\n" + " raise TypeError('yeet')\n" + "\n" + "def py_return_error():\n" + " return BaseException('asdf')\n" + "\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("py_throw_error"); + + struct metacall_exception_type ex; + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("yeet", ex.message); + + EXPECT_STREQ("TypeError", ex.label); + + metacall_value_destroy(ret); + + ret = metacall("py_return_error"); + + EXPECT_EQ((int)0, (int)metacall_error_from_value(ret, &ex)); + + EXPECT_STREQ("asdf", ex.message); + + EXPECT_STREQ("BaseException", ex.label); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_fail_test/CMakeLists.txt b/source/tests/metacall_python_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..d16a571d28 --- /dev/null +++ b/source/tests/metacall_python_fail_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_fail_test/source/main.cpp b/source/tests/metacall_python_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp b/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp new file mode 100644 index 0000000000..7ef4531f60 --- /dev/null +++ b/source/tests/metacall_python_fail_test/source/metacall_python_fail_test.cpp @@ -0,0 +1,97 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char buffer[] = + "def sumList(list: [int]):\n" + " return sum(list)\n"; + + // Apparently this loads the function but it does not understand the type + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + enum metacall_value_id id; + + EXPECT_EQ((int)0, (int)metacall_function_parameter_type(metacall_function("sumList"), 0, &id)); + + // The type of list must be invalid once it loads + EXPECT_EQ((enum metacall_value_id)METACALL_INVALID, (enum metacall_value_id)id); + + const char *py_scripts[] = { + "this_does_not_exists_yeet.py" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char buffer_fail[] = + "def sdf: as asf return"; + + // This must fail + EXPECT_EQ((int)1, (int)metacall_load_from_memory("py", buffer_fail, sizeof(buffer_fail), NULL)); + + const char *py_scripts_non_installed[] = { + "badimport.py" + }; + + // Print traceback + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_non_installed, sizeof(py_scripts_non_installed) / sizeof(py_scripts_non_installed[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_gc_test/CMakeLists.txt b/source/tests/metacall_python_gc_test/CMakeLists.txt index 89c1554110..b726024342 100644 --- a/source/tests/metacall_python_gc_test/CMakeLists.txt +++ b/source/tests/metacall_python_gc_test/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if this loader is enabled -if(NOT OPTION_BUILD_DIST_LIBS OR NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) return() endif() @@ -59,9 +59,8 @@ add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Dependecies # -# Add metacall distributable dependency add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -94,9 +93,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable + ${META_PROJECT_NAME}::metacall ) # @@ -117,11 +114,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -134,6 +140,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_gc_test/source/main.cpp b/source/tests/metacall_python_gc_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_python_gc_test/source/main.cpp +++ b/source/tests/metacall_python_gc_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp b/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp index c41b6a0a7f..8bdea252fb 100644 --- a/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp +++ b/source/tests/metacall_python_gc_test/source/metacall_python_gc_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,29 +32,38 @@ TEST_F(metacall_python_gc_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "garbage.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("set_debug")); + void *ret = metacall("set_debug"); - void * ret = metacall("garbage"); + ASSERT_NE((void *)NULL, (void *)ret); - ASSERT_NE((void *) NULL, (void *) ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); + + ret = metacall("garbage"); + + ASSERT_NE((void *)NULL, (void *)ret); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); std::cout << metacall_value_to_string(ret) << std::endl; metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_loader_port_test/CMakeLists.txt b/source/tests/metacall_python_loader_port_test/CMakeLists.txt new file mode 100644 index 0000000000..eed75ba06a --- /dev/null +++ b/source/tests/metacall_python_loader_port_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_RB OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target py-loader-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_loader_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_loader_port_test/source/main.cpp b/source/tests/metacall_python_loader_port_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_loader_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_loader_port_test/source/metacall_python_loader_port_test.cpp b/source/tests/metacall_python_loader_port_test/source/metacall_python_loader_port_test.cpp new file mode 100644 index 0000000000..ffa9043263 --- /dev/null +++ b/source/tests/metacall_python_loader_port_test/source/metacall_python_loader_port_test.cpp @@ -0,0 +1,135 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_loader_port_test : public testing::Test +{ +public: +}; + +void *callback_host(size_t argc, void *args[], void *data) +{ + const char *str = metacall_value_cast_string(&args[0]); + + (void)argc; + (void)data; + + printf("Host callback: %s\n", str); + + EXPECT_STREQ(str, "some text"); + + return metacall_value_create_int(25); +} + +TEST_F(metacall_python_loader_port_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Native register */ + { + metacall_register("callback_host_impl", callback_host, NULL, METACALL_INT, 1, METACALL_STRING); + + EXPECT_NE((void *)NULL, (void *)metacall_function("callback_host_impl")); + } + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "hello.rb", "second.rb" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + ret = metacall("say_multiply", 5, 7); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)35, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + + ret = metacall("say_null"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + ret = metacall("say_hello", "meta-programmer"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_cast_string(&ret), "Hello meta-programmer!"); + + metacall_value_destroy(ret); + + EXPECT_NE((void *)NULL, (void *)metacall_function("get_second")); + + ret = metacall("get_second", 5, 12); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)12, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "callback.py" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + ret = metacall("hello_world", "some text"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)25, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + + ret = metacall("hello_ruby", 12, 4); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)48, (int)metacall_value_cast_int(&ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_model_test/CMakeLists.txt b/source/tests/metacall_python_model_test/CMakeLists.txt index a46ba7fd2b..01475c8c83 100644 --- a/source/tests/metacall_python_model_test/CMakeLists.txt +++ b/source/tests/metacall_python_model_test/CMakeLists.txt @@ -3,6 +3,18 @@ if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_L return() endif() +# Check if python dependencies are installed +execute_process( + COMMAND python -c "import scipy; import numpy; import scikit-learn; import joblib" + ERROR_QUIET + OUTPUT_QUIET + RESULT_VARIABLE PYTHON_DEPENDS +) + +if(PYTHON_DEPENDS) + return() +endif() + # # Executable name and options # @@ -85,19 +97,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +118,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -136,6 +144,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_model_test/source/main.cpp b/source/tests/metacall_python_model_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_python_model_test/source/main.cpp +++ b/source/tests/metacall_python_model_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp b/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp index 8fddbc34c4..ca200e45b8 100644 --- a/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp +++ b/source/tests/metacall_python_model_test/source/metacall_python_model_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,28 +32,26 @@ TEST_F(metacall_python_model_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "model.py" }; - const enum metacall_value_id ids[] = - { + const enum metacall_value_id ids[] = { METACALL_DOUBLE }; - ASSERT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = metacallt("predict_salary", ids, 5.0); + void *ret = metacallt("predict_salary", ids, 5.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - void ** salary_array = metacall_value_to_array(ret); + void **salary_array = metacall_value_to_array(ret); double salary = metacall_value_to_double(salary_array[0]); @@ -61,7 +59,7 @@ TEST_F(metacall_python_model_test, DefaultConstructor) metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -69,13 +67,13 @@ TEST_F(metacall_python_model_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -84,5 +82,5 @@ TEST_F(metacall_python_model_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_node_await_test/CMakeLists.txt b/source/tests/metacall_python_node_await_test/CMakeLists.txt new file mode 100644 index 0000000000..e2d89c8ffc --- /dev/null +++ b/source/tests/metacall_python_node_await_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-node-await-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_node_await_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(PY_DEBUG_ENVIRONMENT_VARIABLES + PYTHONTHREADDEBUG=1 + PYTHONASYNCIODEBUG=1 + ) +else() + set(PY_DEBUG_ENVIRONMENT_VARIABLES) +endif() + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${PY_DEBUG_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_node_await_test/source/main.cpp b/source/tests/metacall_python_node_await_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_node_await_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_node_await_test/source/metacall_python_node_await_test.cpp b/source/tests/metacall_python_node_await_test/source/metacall_python_node_await_test.cpp new file mode 100644 index 0000000000..168f3517c4 --- /dev/null +++ b/source/tests/metacall_python_node_await_test/source/metacall_python_node_await_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_node_await_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_node_await_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* NodeJS & Python */ +#if defined(OPTION_BUILD_LOADERS_NODE) && defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import threading\n" + "import asyncio\n" + "from metacall import metacall_load_from_memory, metacall_await\n" + "script = \"\"\"\n" + "async function yeet(n) {\n" + " return n;\n" + "}\n" + "module.exports = { yeet }\n" + "\"\"\"\n" + "result = 0\n" + "lock = threading.Lock()\n" + "metacall_load_from_memory('node', script)\n" + "async def test():\n" + " result = await metacall_await('yeet', 1234)\n" + " if result != 1234:\n" + " sys.exit(1)\n" + " else:\n" + " lock.acquire()\n" + " result = 1\n" + " lock.release()\n" + "asyncio.run(test())\n" + "lock.acquire()\n" + "if result != 1:\n" + " sys.exit(2)\n" + "lock.release()\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE && OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_object_class_test/CMakeLists.txt b/source/tests/metacall_python_object_class_test/CMakeLists.txt new file mode 100644 index 0000000000..f81c535ee8 --- /dev/null +++ b/source/tests/metacall_python_object_class_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-object-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_object_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_object_class_test/source/main.cpp b/source/tests/metacall_python_object_class_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_object_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_object_class_test/source/metacall_python_object_class_test.cpp b/source/tests/metacall_python_object_class_test/source/metacall_python_object_class_test.cpp new file mode 100644 index 0000000000..092c58927c --- /dev/null +++ b/source/tests/metacall_python_object_class_test/source/metacall_python_object_class_test.cpp @@ -0,0 +1,225 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "classname.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *return_itself_args[] = { + myclass_value + }; + void *return_itself = metacallv("return_itself", return_itself_args); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(return_itself)); + metacall_value_destroy(return_itself); + metacall_value_destroy(myclass_value); + } + + { + void *myobject_value = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(myobject_value)); + void *return_itself_args[] = { + myobject_value + }; + void *return_itself = metacallv("return_itself", return_itself_args); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(return_itself)); + metacall_value_destroy(return_itself); + metacall_value_destroy(myobject_value); + } + + { + void *ret = metacall("function_returns_object_new_local_variable"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + } + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *myclass = metacall_value_to_class(myclass_value); + + static const char john[] = "John Doe"; + + void *constructor_params[] = { + metacall_value_create_string(john, sizeof(john) - 1), // param1 + metacall_value_create_int(999999) // param2 + }; + void *new_object_v = metacall_class_new(myclass, "objectname", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + metacall_value_destroy(constructor_params[1]); + void *new_object = metacall_value_to_object(new_object_v); + + void *param2 = metacall_object_get(new_object, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)999999, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + void *long_value = metacall_value_create_long(124124L); + int retcode = metacall_object_set(new_object, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_object_get(new_object, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)124124L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + metacall_value_destroy(new_object_v); + metacall_value_destroy(myclass_value); + } + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *myclass = metacall_value_to_class(myclass_value); + + void *param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)20L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + void *long_value = metacall_value_create_long(44444L); + int retcode = metacall_class_static_set(myclass, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)44444L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + metacall_value_destroy(myclass_value); + } + + { + void *myclass = metacall_class("MyClass"); + ASSERT_NE((void *)NULL, (void *)myclass); + + static const char works[] = "It works!"; + + void *static_method_args[] = { + metacall_value_create_string(works, sizeof(works) - 1) + }; + void *ret_value = metacallv_class(myclass, "static", static_method_args, sizeof(static_method_args) / sizeof(static_method_args[0])); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret_value)); + metacall_value_destroy(static_method_args[0]); + metacall_value_destroy(ret_value); + + // Get and Set + void *param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)44444L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + + void *long_value = metacall_value_create_long(5555L); + int retcode = metacall_class_static_set(myclass, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_class_static_get(myclass, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)5555L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + } + + { + void *obj_value = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(obj_value)); + void *obj = metacall_value_to_object(obj_value); + + static const char john[] = "John Doe"; + + void *return_bye_args[] = { + metacall_value_create_string(john, sizeof(john) - 1) + }; + void *ret = metacallv_object(obj, "return_bye", return_bye_args, sizeof(return_bye_args) / sizeof(return_bye_args[0])); + metacall_value_destroy(return_bye_args[0]); + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + void *return_check_args[] = { + metacall_value_create_long(4L), + metacall_value_create_long(7L) + }; + ret = metacallv_object(obj, "check_args", return_check_args, sizeof(return_check_args) / sizeof(return_check_args[0])); + metacall_value_destroy(return_check_args[0]); + metacall_value_destroy(return_check_args[1]); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((long)15L, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + + metacall_value_destroy(obj_value); + } + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_open_test/CMakeLists.txt b/source/tests/metacall_python_open_test/CMakeLists.txt index 4b538912a9..32959b3635 100644 --- a/source/tests/metacall_python_open_test/CMakeLists.txt +++ b/source/tests/metacall_python_open_test/CMakeLists.txt @@ -55,6 +55,27 @@ add_executable(${target} # Create namespaced alias add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) +# +# Dependencies +# + +if(NOT OPTION_BUILD_GUIX) + find_package(NPM) + + if(NOT NPM_FOUND) + message(SEND_ERROR "NPM not found") + return() + endif() + + add_custom_target(${target}-depends + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${NPM_EXECUTABLE} --prefix ${CMAKE_CURRENT_BINARY_DIR} install jsonwebtoken + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/node_modules ${PROJECT_OUTPUT_DIR}/node_modules + ) + + add_dependencies(${target} ${target}-depends) +endif() + # # Project options # @@ -85,19 +106,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -119,11 +127,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -132,10 +149,60 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13743) + # #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x87323) + # #1 std::__new_allocator::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137 (libbacktrace_plugind.so+0x7096) + # #2 std::allocator_traits >::allocate(std::allocator&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464 (libbacktrace_plugind.so+0x7096) + # #3 std::_Vector_base >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378 (libbacktrace_plugind.so+0x7096) + # #4 std::vector >::_M_default_append(unsigned long) /usr/include/c++/12/bits/vector.tcc:650 (libbacktrace_plugind.so+0x7096) + # #5 std::vector >::resize(unsigned long) /usr/include/c++/12/bits/stl_vector.h:1011 (libbacktrace_plugind.so+0x7453) + # #6 backward::StackTraceImpl::load_here(unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:879 (libbacktrace_plugind.so+0x7453) + # #7 backward::StackTraceImpl::load_from(void*, unsigned long, void*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:887 (libbacktrace_plugind.so+0xe4da) + # #8 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4249 (libbacktrace_plugind.so+0xe4da) + # #9 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #10 (libcoreclr.so+0x4afbdc) + # #11 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #12 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #13 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 py_loader_port_load_from_file_impl /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:190 (libpy_loaderd.so+0x10a02) + # #18 py_loader_port_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:238 (libpy_loaderd.so+0x10cde) + # #19 (libpython3.9.so.1.0+0x10cc73) + # #20 py_loader_impl_load_from_file_relative /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3102 (libpy_loaderd.so+0xb5ae) + # #21 py_loader_impl_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3193 (libpy_loaderd.so+0xb7db) + # #22 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:838 (libmetacalld.so+0x30944) + # #23 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #24 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #25 metacall_python_open_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp:44 (metacall-python-open-testd+0x20d18) + # #26 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-python-open-testd+0x57646) + # #27 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 in operator new(unsigned long) + # + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader + cs_loader +) + # # Define test properties # @@ -144,6 +211,19 @@ set_property(TEST ${target} PROPERTY LABELS ${target} ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizers (this happens when C# loader is enabled): + # Tracer caught signal 11: addr=0x1500000aa8 pc=0x7f49da2cc0f0 sp=0x7f49d4ad2d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_python_open_test/source/main.cpp b/source/tests/metacall_python_open_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_python_open_test/source/main.cpp +++ b/source/tests/metacall_python_open_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp b/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp index afdd42b3b7..0174eb61f5 100644 --- a/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp +++ b/source/tests/metacall_python_open_test/source/metacall_python_open_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -32,29 +32,46 @@ TEST_F(metacall_python_open_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "landing.py" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - void * ret = metacall("index"); + void *ret = metacall("index"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - const char * result = metacall_value_to_string(ret); + const char *result = metacall_value_to_string(ret); - EXPECT_NE((int) 0, (int) strcmp(result, "Error")); + EXPECT_STRNE(result, "Error"); + + metacall_value_destroy(ret); + + static const char str[] = "Hello World"; + + void *args[] = { + metacall_value_create_string(str, sizeof(str) - 1) + }; + + ret = metacallv("login", args); + + EXPECT_NE((void *)NULL, (void *)ret); + + const char *token = metacall_value_to_string(ret); + + EXPECT_STREQ(token, "eyJhbGciOiJIUzI1NiJ9.SGVsbG8gV29ybGQ.Iyc6PWVbK538giVdaInTeIO3jvvC1Vuy_czZUzoRRec"); + + metacall_value_destroy(args[0]); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -62,13 +79,13 @@ TEST_F(metacall_python_open_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -77,5 +94,5 @@ TEST_F(metacall_python_open_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_pointer_test/CMakeLists.txt b/source/tests/metacall_python_pointer_test/CMakeLists.txt index 635b9b9603..fe6f4ee616 100644 --- a/source/tests/metacall_python_pointer_test/CMakeLists.txt +++ b/source/tests/metacall_python_pointer_test/CMakeLists.txt @@ -3,18 +3,6 @@ if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_S return() endif() -# -# External dependencies -# - -# MetaCall Library -find_package(MetaCall REQUIRED) - -if(NOT METACALL_FOUND) - message(STATUS "MetaCall libraries not found") - return() -endif() - # # Executable name and options # @@ -85,8 +73,6 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include - - ${METACALL_INCLUDE_DIRS} # MetaCall includes ) # @@ -99,9 +85,7 @@ target_link_libraries(${target} GTest - ${CMAKE_DL_LIBS} - - ${METACALL_LIBRARIES} # MetaCall libraries + ${META_PROJECT_NAME}::metacall ) # @@ -122,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -139,6 +132,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # diff --git a/source/tests/metacall_python_pointer_test/source/main.cpp b/source/tests/metacall_python_pointer_test/source/main.cpp index ca664715dc..7832a63230 100644 --- a/source/tests/metacall_python_pointer_test/source/main.cpp +++ b/source/tests/metacall_python_pointer_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp b/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp index 7d425da848..cd7edd2e2d 100644 --- a/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp +++ b/source/tests/metacall_python_pointer_test/source/metacall_python_pointer_test.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading python code at run-time into a process. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -34,11 +34,14 @@ struct test_type unsigned char r, g, b; }; -void * native_set_value(void * args[]) +void *native_set_value(size_t argc, void *args[], void *data) { - struct test_type * t = (struct test_type *)metacall_value_to_ptr(args[0]); + struct test_type *t = (struct test_type *)metacall_value_to_ptr(args[0]); long value = metacall_value_to_long(args[1]); + (void)argc; + (void)data; + printf("Pointer: %p\n", (void *)t); printf("Value: %ld\n", value); @@ -51,12 +54,11 @@ void * native_set_value(void * args[]) return metacall_value_create_ptr((void *)t); } -void * native_get_value(void * args[]) +void *native_get_value(size_t argc, void *args[], void *data) { - struct test_type * t = (struct test_type *)metacall_value_to_ptr(args[0]); + struct test_type *t = (struct test_type *)metacall_value_to_ptr(args[0]); - const void * array[] = - { + const void *array[] = { metacall_value_create_char(t->r), metacall_value_create_char(t->g), metacall_value_create_char(t->b) @@ -64,65 +66,99 @@ void * native_get_value(void * args[]) const size_t size = sizeof(array) / sizeof(array[0]); + (void)argc; + (void)data; + printf("Array Size: %zu\n", size); return metacall_value_create_array(array, size); } +void *native_ret_null_ptr(size_t argc, void *args[], void *data) +{ + void *ptr = metacall_value_to_ptr(args[0]); + + EXPECT_EQ((void *)ptr, (void *)NULL); + + (void)argc; + (void)data; + + return metacall_value_create_ptr(NULL); +} + TEST_F(metacall_python_pointer_test, DefaultConstructor) { metacall_print_info(); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); /* Native register */ { - metacall_register("native_set_value", native_set_value, METACALL_PTR, 2, METACALL_PTR, METACALL_LONG); + metacall_register("native_set_value", native_set_value, NULL, METACALL_PTR, 2, METACALL_PTR, METACALL_LONG); + + EXPECT_NE((void *)NULL, (void *)metacall_function("native_set_value")); - EXPECT_NE((void *) NULL, (void *) metacall_function("native_set_value")); + metacall_register("native_get_value", native_get_value, NULL, METACALL_ARRAY, 1, METACALL_PTR); - metacall_register("native_get_value", native_get_value, METACALL_ARRAY, 1, METACALL_PTR); + EXPECT_NE((void *)NULL, (void *)metacall_function("native_get_value")); - EXPECT_NE((void *) NULL, (void *) metacall_function("native_get_value")); + metacall_register("native_ret_null_ptr", native_ret_null_ptr, NULL, METACALL_PTR, 0); + + EXPECT_NE((void *)NULL, (void *)metacall_function("native_ret_null_ptr")); } - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "pointer.py" }; - void * ret = NULL; + void *ret = NULL; struct test_type t = { 0L, 0U, 0U, 0U }; - void * t_ptr = (void *)&t; + void *t_ptr = (void *)&t; long value = 3000L; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - const enum metacall_value_id ids[] = - { + const enum metacall_value_id ids[] = { METACALL_PTR, METACALL_LONG }; ret = metacallt("python_set_value", ids, t_ptr, value); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_PTR, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)t_ptr, (void *)metacall_value_to_ptr(ret)); + + EXPECT_EQ((long)value, (long)t.value); + EXPECT_EQ((unsigned char)10U, (unsigned char)t.r); + EXPECT_EQ((unsigned char)50U, (unsigned char)t.g); + EXPECT_EQ((unsigned char)70U, (unsigned char)t.b); + + metacall_value_destroy(ret); + + void *args[] = { + metacall_value_create_ptr(NULL) + }; + + ret = metacallv("python_ret_null", args); + + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((void *) t_ptr, (void *) metacall_value_cast_ptr(&ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_PTR, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((long) value, (long) t.value); - EXPECT_EQ((unsigned char) 10U, (unsigned char) t.r); - EXPECT_EQ((unsigned char) 50U, (unsigned char) t.g); - EXPECT_EQ((unsigned char) 70U, (unsigned char) t.b); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_ptr(ret)); metacall_value_destroy(ret); + metacall_value_destroy(args[0]); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_python_port_callback_test/CMakeLists.txt b/source/tests/metacall_python_port_callback_test/CMakeLists.txt new file mode 100644 index 0000000000..bbf27070a8 --- /dev/null +++ b/source/tests/metacall_python_port_callback_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-callback-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_callback_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + c_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_port_callback_test/source/main.cpp b/source/tests/metacall_python_port_callback_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_callback_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_callback_test/source/metacall_python_port_callback_test.cpp b/source/tests/metacall_python_port_callback_test/source/metacall_python_port_callback_test.cpp new file mode 100644 index 0000000000..c0fd5e5b21 --- /dev/null +++ b/source/tests/metacall_python_port_callback_test/source/metacall_python_port_callback_test.cpp @@ -0,0 +1,71 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_callback_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_callback_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "from metacall import metacall_load_from_file, metacall\n" + "def sum_cb(a: int, b: int) -> int:\n" + " return a + b\n" + "def v_cb():\n" + " print('hello world')\n" + "def str_cb() -> str:\n" + " return 'hello world'\n" + "def test() -> int:\n" + " metacall_load_from_file('c', ['cbks.c'])\n" + " metacall('c_void_callback', v_cb)\n" + " return metacall('c_long_callback', sum_cb)\n"; + + // TODO: + // " s = metacall('c_str_callback', str_cb)\n" + // " print('----------------------')\n" + // " print(s)\n" + // " print('----------------------')\n" + // " return metacall('c_long_callback', sum_cb) + len(s)\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("test"); + + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)7L, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_https_test/CMakeLists.txt b/source/tests/metacall_python_port_https_test/CMakeLists.txt new file mode 100644 index 0000000000..1f1335666e --- /dev/null +++ b/source/tests/metacall_python_port_https_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-https-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_https_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + rb_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_port_https_test/source/main.cpp b/source/tests/metacall_python_port_https_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_https_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_https_test/source/metacall_python_port_https_test.cpp b/source/tests/metacall_python_port_https_test/source/metacall_python_port_https_test.cpp new file mode 100644 index 0000000000..e6082d2fe9 --- /dev/null +++ b/source/tests/metacall_python_port_https_test/source/metacall_python_port_https_test.cpp @@ -0,0 +1,75 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_https_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_https_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + // Test import bug (__metacall_import__() missing 1 required positional argument: 'name') + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from http import client\n" + "def fetch_http_py(url: str):\n" + " try:\n" + " conn = client.HTTPSConnection(url, 443)\n" + " conn.request('GET', '/')\n" + " response = conn.getresponse()\n" + " data = response.read()\n" + " conn.close()\n" + " return data\n" + " except Exception as e:\n" + " print(e)\n" + " sys.stdout.flush()\n" + " return b''\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("fetch_http_py", "www.google.com"); + + static const char prefix[] = ""; + + ASSERT_EQ((enum metacall_value_id)METACALL_BUFFER, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((int)0, (int)strncmp((const char *)metacall_value_to_buffer(ret), prefix, sizeof(prefix) - 1)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_import_test/CMakeLists.txt b/source/tests/metacall_python_port_import_test/CMakeLists.txt new file mode 100644 index 0000000000..051f059533 --- /dev/null +++ b/source/tests/metacall_python_port_import_test/CMakeLists.txt @@ -0,0 +1,198 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-import-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_import_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port Test path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13823) + # #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:706 (libtsan.so.2+0x47e82) + # #1 (libdw.so.1+0x56c62) + # #2 backward::TraceResolverImpl::~TraceResolverImpl() /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:3479 (libbacktrace_plugind.so+0xf95c) + # #3 backward::TraceResolver::~TraceResolver() /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:3694 (libbacktrace_plugind.so+0xf95c) + # #4 backward::Printer::~Printer() /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:3986 (libbacktrace_plugind.so+0xf95c) + # #5 backward::SignalHandling::handleSignal(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4265 (libbacktrace_plugind.so+0xf95c) + # #6 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4276 (libbacktrace_plugind.so+0xfff0) + # #7 (libcoreclr.so+0x4afbdc) + # #8 backward::SignalHandling::sig_handler(int, siginfo_t*, void*) /usr/local/metacall/build/_deps/backwardcpp-src/backward.hpp:4279 (libbacktrace_plugind.so+0xffff) + # #9 (libcoreclr.so+0x4afbdc) + # #10 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #11 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #12 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x306a3) + # #13 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x308b8) + # #14 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e101) + # #15 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bef) + # #16 py_loader_port_load_from_file_impl /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:190 (libpy_loaderd.so+0x10a02) + # #17 py_loader_port_load_from_file_export /usr/local/metacall/source/loaders/py_loader/source/py_loader_port.c:244 (libpy_loaderd.so+0x10d34) + # #18 (libpython3.9.so.1.0+0x10cc73) + # #19 loader_impl_load_from_memory /usr/local/metacall/source/loader/source/loader_impl.c:968 (libmetacalld.so+0x30eba) + # #20 loader_load_from_memory /usr/local/metacall/source/loader/source/loader.c:327 (libmetacalld.so+0x2e201) + # #21 metacall_load_from_memory /usr/local/metacall/source/metacall/source/metacall.c:357 (libmetacalld.so+0x32c30) + # #22 metacall_python_port_import_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp:48 (metacall-python-port-import-testd+0x20aa5) + # #23 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-python-port-import-testd+0x4e186) + # #24 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/usr/lib/x86_64-linux-gnu/libdw.so.1+0x56c62) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/ramda +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + "LOADER_SCRIPT_PATH=${LOADER_SCRIPT_PATH}/ramda" +) diff --git a/source/tests/metacall_python_port_import_test/source/main.cpp b/source/tests/metacall_python_port_import_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_import_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp b/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp new file mode 100644 index 0000000000..23b93ca1a4 --- /dev/null +++ b/source/tests/metacall_python_port_import_test/source/metacall_python_port_import_test.cpp @@ -0,0 +1,122 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_import_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_import_test, metacall_node_ramda_case_1) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Case 0 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from asd.mock import two_doubles\n" + "if two_doubles(3.0, 6.0) != 3.1416:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 1 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "import metacall.node.ramda\n" + "if metacall.node.ramda.all(metacall.node.ramda.equals(3))([3, 3, 3, 3]) != True:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 2 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from metacall.node.ramda import equals, all\n" + "if all(equals(3))([3, 3, 3, 3]) != True:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 2 star */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from metacall.node.ramda import *\n" + "if all(equals(3))([3, 3, 3, 3]) != True:\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + /* Case 3 */ + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "import metacall\n" + "from metacall.node import ramda, path\n" + "if ramda.all(ramda.equals(3))([3, 3, 3, 3]) != True or path.extname('index.html') != '.html':\n" + " sys.exit(1)\n"; + + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), &handle)); + + ASSERT_NE((void *)handle, (void *)NULL); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_pointer_test/CMakeLists.txt b/source/tests/metacall_python_port_pointer_test/CMakeLists.txt new file mode 100644 index 0000000000..2c4133672e --- /dev/null +++ b/source/tests/metacall_python_port_pointer_test/CMakeLists.txt @@ -0,0 +1,161 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_C OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-pointer-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_pointer_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + c_loader + loadtest +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_port_pointer_test/source/main.cpp b/source/tests/metacall_python_port_pointer_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_pointer_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp b/source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp new file mode 100644 index 0000000000..fd6214d1d5 --- /dev/null +++ b/source/tests/metacall_python_port_pointer_test/source/metacall_python_port_pointer_test.cpp @@ -0,0 +1,122 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_pointer_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_pointer_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* Test value reference and dereference */ + void *v = metacall_value_create_int(34551); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(v)); + + void *ref = metacall_value_reference(v); + + ASSERT_EQ((enum metacall_value_id)METACALL_PTR, (enum metacall_value_id)metacall_value_id(ref)); + + int *int_ptr = (int *)metacall_value_to_ptr(ref); + + *int_ptr += 10; + + void *result = metacall_value_dereference(ref); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(result)); + + ASSERT_EQ((int)34561, (int)metacall_value_to_int(result)); + + ASSERT_EQ((void *)v, (void *)result); + + /* Test Python reference and dereference */ + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_PORT_PATH "')\n" + "from metacall import metacall_load_from_package, metacall, metacall_value_create_ptr, metacall_value_reference, metacall_value_dereference\n" + "metacall_load_from_package('c', 'loadtest')\n" + + "def test_int_ptr() -> int:\n" + " print('Test start')\n" + " sys.stdout.flush()\n" + + " int_val = 324444\n" + " int_val_ref = metacall_value_reference(int_val)\n" + + " print(int_val_ref)\n" + " sys.stdout.flush()\n" + + " metacall('modify_int_ptr', int_val_ref)\n" + " int_val_deref = metacall_value_dereference(int_val_ref)\n" + + " print(int_val, '!=', int_val_deref)\n" + " sys.stdout.flush()\n" + + " return int_val_deref\n" + + "def test_struct_ptr() -> float:\n" + " print('Test start')\n" + " sys.stdout.flush()\n" + + " list_pair = metacall_value_create_ptr(None)\n" + " list_pair_ref = metacall_value_reference(list_pair)\n" + " result = metacall('pair_list_init', list_pair_ref)\n" + " print(result)\n" + " sys.stdout.flush()\n" + + " list_pair = metacall_value_dereference(list_pair_ref)\n" + " result = metacall('pair_list_value', list_pair, 2)\n" + " print(result)\n" + " sys.stdout.flush()\n" + + " metacall('pair_list_destroy', list_pair)\n" + + " return result\n"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("py", buffer, sizeof(buffer), NULL)); + + void *ret = metacall("test_int_ptr"); + + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((long)111L, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + ret = metacall("test_struct_ptr"); + + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((double)2.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_port_test/CMakeLists.txt b/source/tests/metacall_python_port_test/CMakeLists.txt new file mode 100644 index 0000000000..124502f07b --- /dev/null +++ b/source/tests/metacall_python_port_test/CMakeLists.txt @@ -0,0 +1,170 @@ +# Check if loaders are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_RB OR NOT OPTION_BUILD_SCRIPTS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port Test path + METACALL_PYTHON_PORT_TEST_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port/test.py" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# Enable rust test if it is built +if(OPTION_BUILD_LOADERS_RS) + set(RS_DEPENDENCY rs_loader) + set(TESTS_ENVIRONMENT_VARIABLES_RS "OPTION_BUILD_LOADERS_RS=1") +endif() + +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader + py_loader + rb_loader + node_loader + ${RS_DEPENDENCY} +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + ${TESTS_ENVIRONMENT_VARIABLES_RS} +) diff --git a/source/tests/metacall_python_port_test/source/main.cpp b/source/tests/metacall_python_port_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_port_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_port_test/source/metacall_python_port_test.cpp b/source/tests/metacall_python_port_test/source/metacall_python_port_test.cpp new file mode 100644 index 0000000000..7ebb7c236e --- /dev/null +++ b/source/tests/metacall_python_port_test/source/metacall_python_port_test.cpp @@ -0,0 +1,56 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_python_port_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_port_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char *py_scripts[] = { + METACALL_PYTHON_PORT_TEST_PATH + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + void *ret = metacallv("main", metacall_null_args); + + EXPECT_STREQ(metacall_value_to_string(ret), "Tests passed without errors"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_reentrant_test/CMakeLists.txt b/source/tests/metacall_python_reentrant_test/CMakeLists.txt new file mode 100644 index 0000000000..ab29592ef2 --- /dev/null +++ b/source/tests/metacall_python_reentrant_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-reentrant-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_reentrant_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + + # Python Port path + METACALL_PYTHON_REENTRANT_TEST_PY_PORT_PATH="${CMAKE_SOURCE_DIR}/source/ports/py_port" + +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_reentrant_test/source/main.cpp b/source/tests/metacall_python_reentrant_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_reentrant_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp b/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp new file mode 100644 index 0000000000..ae1d107591 --- /dev/null +++ b/source/tests/metacall_python_reentrant_test/source/metacall_python_reentrant_test.cpp @@ -0,0 +1,80 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#ifndef METACALL_PYTHON_REENTRANT_TEST_PY_PORT_PATH + #error "The path to the Python port is not defined" +#endif + +class metacall_python_reentrant_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_reentrant_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + static const char buffer[] = + "import sys\n" + "sys.path.insert(0, '" METACALL_PYTHON_REENTRANT_TEST_PY_PORT_PATH "');\n" + "from metacall import metacall_load_from_memory, metacall\n" + "metacall_load_from_memory('py', 'def python_memory(): return 4;');\n" + "print('Reentrant python_memory result:', metacall('python_memory'));\n"; + + static const char tag[] = "py"; + + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_relative_path_test/CMakeLists.txt b/source/tests/metacall_python_relative_path_test/CMakeLists.txt new file mode 100644 index 0000000000..22cae4f281 --- /dev/null +++ b/source/tests/metacall_python_relative_path_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-relative-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_relative_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_relative_path_test/source/main.cpp b/source/tests/metacall_python_relative_path_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_relative_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_relative_path_test/source/metacall_python_relative_path_test.cpp b/source/tests/metacall_python_relative_path_test/source/metacall_python_relative_path_test.cpp new file mode 100644 index 0000000000..0c44179d68 --- /dev/null +++ b/source/tests/metacall_python_relative_path_test/source/metacall_python_relative_path_test.cpp @@ -0,0 +1,76 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_dict_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_dict_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "./s2.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_fail[] = { + "./yeet/fail/this-script-is-impossible-that-exists.py" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_test/CMakeLists.txt b/source/tests/metacall_python_test/CMakeLists.txt new file mode 100644 index 0000000000..6f89617e34 --- /dev/null +++ b/source/tests/metacall_python_test/CMakeLists.txt @@ -0,0 +1,157 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + PY_LOADER_TEST_SCRIPT_ABSOLUTE_PATH="${LOADER_SCRIPT_PATH}/dicty.py" +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_test/source/main.cpp b/source/tests/metacall_python_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_test/source/metacall_python_test.cpp b/source/tests/metacall_python_test/source/metacall_python_test.cpp new file mode 100644 index 0000000000..690bf226af --- /dev/null +++ b/source/tests/metacall_python_test/source/metacall_python_test.cpp @@ -0,0 +1,81 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "example.py", // Classic load + "helloworld.py", // Classic load + "json", // Module load + "os.path", // Submodule load + PY_LOADER_TEST_SCRIPT_ABSOLUTE_PATH, // Absolute load + "./s2.py" // Relative load + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_fail[] = { + "thismoduledoesnotexistaaaa", // Non-existent Module load + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_varargs_test/CMakeLists.txt b/source/tests/metacall_python_varargs_test/CMakeLists.txt new file mode 100644 index 0000000000..f8636d21e2 --- /dev/null +++ b/source/tests/metacall_python_varargs_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) +return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-varargs-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_varargs_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_varargs_test/source/main.cpp b/source/tests/metacall_python_varargs_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_varargs_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_varargs_test/source/metacall_python_varargs_test.cpp b/source/tests/metacall_python_varargs_test/source/metacall_python_varargs_test.cpp new file mode 100644 index 0000000000..e640a1f50f --- /dev/null +++ b/source/tests/metacall_python_varargs_test/source/metacall_python_varargs_test.cpp @@ -0,0 +1,106 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_varargs_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_varargs_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char python_script[] = + "#!/usr/bin/env python3\n" + "values = [10, 20, 30]\n" + "def varargs(*args):\n" + " for (v, a) in zip(values, args):\n" + " print(v, ' == ', a);\n" + " if v != a:\n" + " return 324;\n" + " return 20;\n"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", python_script, sizeof(python_script), NULL)); + + void *args2[] = { + metacall_value_create_long(10), + metacall_value_create_long(20) + }; + + void *args3[] = { + metacall_value_create_long(10), + metacall_value_create_long(20), + metacall_value_create_long(30) + }; + + void *ret = metacallv_s("varargs", args2, 2); + + ASSERT_EQ((long)20, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + ret = metacallv_s("varargs", args3, 3); + + ASSERT_EQ((long)20, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + metacall_value_destroy(args2[0]); + metacall_value_destroy(args2[1]); + + metacall_value_destroy(args3[0]); + metacall_value_destroy(args3[1]); + metacall_value_destroy(args3[2]); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt b/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt new file mode 100644 index 0000000000..3aca8756e3 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt @@ -0,0 +1,169 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-without-env-vars-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_without_env_vars_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +if(OPTION_BUILD_ADDRESS_SANITIZER OR OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test leaks and I am not sure if it is a false positive or not: + # + # Direct leak of 18682 byte(s) in 12 object(s) allocated from: + # #0 0x7fa978386bd7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fa9734ddbb1 in _PyMem_RawMalloc ../Objects/obmalloc.c:101 + + # Indirect leak of 2775 byte(s) in 2 object(s) allocated from: + # #0 0x7fa978386bd7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 + # #1 0x7fa9734ddbb1 in _PyMem_RawMalloc ../Objects/obmalloc.c:101 + + # SUMMARY: AddressSanitizer: 21457 byte(s) leaked in 14 allocation(s). + # + # Valgrind does not show anything: + # valgrind --tool=memcheck --leak-check=full --show-leak-kinds=possibly --track-origins=yes --num-callers=500 --suppressions=../source/tests/memcheck/valgrind-python.supp ./metacall-python-without-env-vars-testd + # For checking all the leaks (and including false positives), check: + # valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes --num-callers=500 --suppressions=../source/tests/memcheck/valgrind-python.supp ./metacall-python-without-env-vars-testd + return() +endif() + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/metacall_python_without_env_vars_test/source/main.cpp b/source/tests/metacall_python_without_env_vars_test/source/main.cpp new file mode 100644 index 0000000000..7832a63230 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp b/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp new file mode 100644 index 0000000000..05ec480890 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp @@ -0,0 +1,49 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_without_env_vars_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_without_env_vars_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts_fail[] = { + "thismoduledoesnotexistaaaa", // Non-existent Module load + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_python_without_functions_test/CMakeLists.txt b/source/tests/metacall_python_without_functions_test/CMakeLists.txt new file mode 100644 index 0000000000..e2092380eb --- /dev/null +++ b/source/tests/metacall_python_without_functions_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) +return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-without-functions-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_without_functions_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_python_without_functions_test/source/main.cpp b/source/tests/metacall_python_without_functions_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_python_without_functions_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_without_functions_test/source/metacall_python_without_functions_test.cpp b/source/tests/metacall_python_without_functions_test/source/metacall_python_without_functions_test.cpp new file mode 100644 index 0000000000..9e84dbe510 --- /dev/null +++ b/source/tests/metacall_python_without_functions_test/source/metacall_python_without_functions_test.cpp @@ -0,0 +1,70 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_without_functions_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_python_without_functions_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "withoutfunctions.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_reinitialize_test/CMakeLists.txt b/source/tests/metacall_reinitialize_test/CMakeLists.txt index 7092a284c3..c1adf593e3 100644 --- a/source/tests/metacall_reinitialize_test/CMakeLists.txt +++ b/source/tests/metacall_reinitialize_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_MOCK) + return() +endif() + # # Executable name and options # @@ -80,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + mock_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_reinitialize_test/source/main.cpp b/source/tests/metacall_reinitialize_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_reinitialize_test/source/main.cpp +++ b/source/tests/metacall_reinitialize_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp b/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp index 382a427158..da47af9905 100644 --- a/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp +++ b/source/tests/metacall_reinitialize_test/source/metacall_reinitialize_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,15 @@ * */ -#include +#include #include -#include #include +#include -void * c_function(void * args[]) +void *c_function(void *args[]) { - printf("%s\n", (char*)args[0]); + printf("%s\n", (char *)args[0]); return metacall_value_create_int(1); } @@ -48,20 +48,19 @@ TEST_F(metacall_reinitialize_test, DefaultConstructor) for (size_t iterator = 0; iterator < initialize_count; ++iterator) { - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } } diff --git a/source/tests/metacall_reload_functions_test/CMakeLists.txt b/source/tests/metacall_reload_functions_test/CMakeLists.txt new file mode 100644 index 0000000000..8d7682d2ce --- /dev/null +++ b/source/tests/metacall_reload_functions_test/CMakeLists.txt @@ -0,0 +1,158 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-reload-functions-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_reload_functions_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader + node_loader + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_reload_functions_test/source/main.cpp b/source/tests/metacall_reload_functions_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_reload_functions_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_reload_functions_test/source/metacall_reload_functions_test.cpp b/source/tests/metacall_reload_functions_test/source/metacall_reload_functions_test.cpp new file mode 100644 index 0000000000..be227cf3d4 --- /dev/null +++ b/source/tests/metacall_reload_functions_test/source/metacall_reload_functions_test.cpp @@ -0,0 +1,174 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_reload_functions_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_reload_functions_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char buffer0[] = + "def f():\n" + " return 5\n"; + + void *handle0 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer0, sizeof(buffer0), &handle0)); + + void *ret = metacallhv_s(handle0, "f", metacall_null_args, 0); + + EXPECT_EQ((long)5, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle0)); + + const char buffer1[] = + "def f():\n" + " return 6\n"; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("py", buffer1, sizeof(buffer1), &handle1)); + + ret = metacallhv_s(handle1, "f", metacall_null_args, 0); + + EXPECT_EQ((long)6, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char buffer0[] = + "def f()\n" + " return 5\n" + "end\n"; + + void *handle0 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer0, sizeof(buffer0), &handle0)); + + void *ret = metacallhv_s(handle0, "f", metacall_null_args, 0); + + EXPECT_EQ((int)5, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle0)); + + const char buffer1[] = + "def f()\n" + " return 6\n" + "end\n"; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rb", buffer1, sizeof(buffer1), &handle1)); + + ret = metacallhv_s(handle1, "f", metacall_null_args, 0); + + EXPECT_EQ((int)6, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char buffer0[] = + "module.exports = {\n" + " f: () => 5,\n" + "};\n"; + + void *handle0 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer0, sizeof(buffer0), &handle0)); + + void *ret = metacallhv_s(handle0, "f", metacall_null_args, 0); + + EXPECT_EQ((double)5.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle0)); + + const char buffer1[] = + "module.exports = {\n" + " f: () => 6,\n" + "};\n"; + + void *handle1 = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("node", buffer1, sizeof(buffer1), &handle1)); + + ret = metacallhv_s(handle1, "f", metacall_null_args, 0); + + EXPECT_EQ((double)6.0, (double)metacall_value_to_double(ret)); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle1)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_return_monad_test/CMakeLists.txt b/source/tests/metacall_return_monad_test/CMakeLists.txt index d3bbc33acb..bcf5ad5c57 100644 --- a/source/tests/metacall_return_monad_test/CMakeLists.txt +++ b/source/tests/metacall_return_monad_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if loaders, scripts and ports are enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + # # Executable name and options # @@ -80,19 +85,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +106,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -131,12 +132,20 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + # # Define test properties # set_property(TEST ${target} - PROPERTY LABELS ${target} MEMCHECK_IGNORE + PROPERTY LABELS ${target} ) include(TestEnvironmentVariables) diff --git a/source/tests/metacall_return_monad_test/source/main.cpp b/source/tests/metacall_return_monad_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_return_monad_test/source/main.cpp +++ b/source/tests/metacall_return_monad_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp b/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp index c5ff547401..5194d3ab86 100644 --- a/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp +++ b/source/tests/metacall_return_monad_test/source/metacall_return_monad_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,15 @@ * */ -#include +#include #include -#include #include +#include -void * c_function(void * args[]) +void *c_function(void *args[]) { - printf("%s\n", (char*)args[0]); + printf("%s\n", (char *)args[0]); return metacall_value_create_int(1); } @@ -42,15 +42,15 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) metacall_log_stdio_type log_stdio = { stdout }; - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_EQ((int)0, (int)metacall_initialize()); - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { static const char buffer[] = - "#!/usr/bin/python3.5\n" + "#!/usr/bin/env python3\n" "def monad(value: int):\n" "\tresult = 'asd';\n" "\tif value > 0:\n" @@ -62,23 +62,23 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - void * ret = NULL; + void *ret = NULL; size_t size = 0; - char * value_str = NULL; + char *value_str = NULL; - ASSERT_EQ((int) 0, (int) metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); + ASSERT_EQ((int)0, (int)metacall_load_from_memory(tag, buffer, sizeof(buffer), NULL)); ret = metacall("monad", 1); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_STRING, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((int) 0, (int) strcmp("asd", metacall_value_to_string(ret))); + EXPECT_STREQ("asd", metacall_value_to_string(ret)); value_str = metacall_serialize(metacall_serial(), ret, &size, allocator); @@ -90,11 +90,11 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) ret = metacall("monad", 0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((enum metacall_value_id) METACALL_BUFFER, (enum metacall_value_id) metacall_value_id(ret)); + EXPECT_EQ((enum metacall_value_id)METACALL_BUFFER, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((int) 0, (int) memcmp(metacall_value_to_buffer(ret), "asd", metacall_value_size(ret))); + EXPECT_EQ((int)0, (int)memcmp(metacall_value_to_buffer(ret), "asd", metacall_value_size(ret))); value_str = metacall_serialize(metacall_serial(), ret, &size, allocator); @@ -106,7 +106,7 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ /* Print inspect information */ { @@ -114,13 +114,13 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -129,5 +129,5 @@ TEST_F(metacall_return_monad_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_rpc_test/CMakeLists.txt b/source/tests/metacall_rpc_test/CMakeLists.txt new file mode 100644 index 0000000000..04925f1f1c --- /dev/null +++ b/source/tests/metacall_rpc_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RPC OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RPC) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rpc-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rpc_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_dependencies(${target} + rpc_loader +) + +# +# Define test +# + +set(NodeJS_EXECUTABLE_ONLY ON) + +find_package(NodeJS) + +if(NOT NodeJS_FOUND) + message(STATUS "NodeJS executable not found, skipping RPC loader test") + return() +endif() + +add_test(NAME ${target} + COMMAND ${NodeJS_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/source/test.js ${CMAKE_CURRENT_SOURCE_DIR}/source/server.js $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_rpc_test/source/main.cpp b/source/tests/metacall_rpc_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_rpc_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp b/source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp new file mode 100644 index 0000000000..670911293b --- /dev/null +++ b/source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp @@ -0,0 +1,91 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_rpc_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_rpc_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* RPC */ +#if defined(OPTION_BUILD_LOADERS_RPC) + { + const char *rpc_scripts[] = { + "remote.url" + }; + + void *handle = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rpc", rpc_scripts, sizeof(rpc_scripts) / sizeof(rpc_scripts[0]), &handle)); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + const enum metacall_value_id divide_ids[] = { + METACALL_FLOAT, METACALL_FLOAT + }; + + void *ret = metacallht_s(handle, "divide", divide_ids, 2, 50.0f, 10.0f); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((float)metacall_value_to_float(ret), (float)5.0f); + + metacall_value_destroy(ret); + + EXPECT_EQ((int)0, (int)metacall_clear(handle)); + + static const char buffer[] = "http://localhost:6094/viferga/example/v1"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rpc", buffer, sizeof(buffer), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RPC */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_rpc_test/source/server.js b/source/tests/metacall_rpc_test/source/server.js new file mode 100644 index 0000000000..f3c7144e36 --- /dev/null +++ b/source/tests/metacall_rpc_test/source/server.js @@ -0,0 +1,60 @@ +const http = require('http'); +const port = 6094; + +const server = http.createServer((req, res) => { + req.on('error', err => { + console.error(err); + process.exit(1); + }); + + res.on('error', err => { + console.error(err); + process.exit(1); + }); + + const data = new Promise((resolve) => { + let body = []; + + req.on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + resolve(Buffer.concat(body).toString()); + }); + }); + + if (req.method === 'GET') { + if (req.url === '/ready') { + res.end('OK'); + return; + } else if (req.url === '/viferga/example/v1/inspect') { + const inspect = '{"py":[{"name":"example.py","scope":{"name":"global_namespace","funcs":[{"name":"divide","signature":{"ret":{"type":{"name":"float","id":6}},"args":[{"name":"left","type":{"name":"float","id":6}},{"name":"right","type":{"name":"float","id":6}}]},"async":false},{"name":"hello","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false},{"name":"return_same_array","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"arr","type":{"name":"","id":18}}]},"async":false},{"name":"bytebuff","signature":{"ret":{"type":{"name":"bytes","id":8}},"args":[{"name":"input","type":{"name":"bytes","id":8}}]},"async":false},{"name":"dont_load_this_function","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"","id":18}},{"name":"right","type":{"name":"","id":18}}]},"async":false},{"name":"sum","signature":{"ret":{"type":{"name":"int","id":4}},"args":[{"name":"left","type":{"name":"int","id":4}},{"name":"right","type":{"name":"int","id":4}}]},"async":false},{"name":"strcat","signature":{"ret":{"type":{"name":"str","id":7}},"args":[{"name":"left","type":{"name":"str","id":7}},{"name":"right","type":{"name":"str","id":7}}]},"async":false},{"name":"return_array","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false},{"name":"multiply","signature":{"ret":{"type":{"name":"int","id":4}},"args":[{"name":"left","type":{"name":"int","id":4}},{"name":"right","type":{"name":"int","id":4}}]},"async":false}],"classes":[],"objects":[]}}],"rb":[{"name":"hello.rb","scope":{"name":"global_namespace","funcs":[{"name":"say_multiply","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"Fixnum","id":3}},{"name":"right","type":{"name":"Fixnum","id":3}}]},"async":false},{"name":"get_second","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"first","type":{"name":"Fixnum","id":3}},{"name":"second","type":{"name":"Fixnum","id":3}}]},"async":false},{"name":"say_null","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false},{"name":"say_hello","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"value","type":{"name":"String","id":7}}]},"async":false},{"name":"backwardsPrime","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"start","type":{"name":"","id":18}},{"name":"stop","type":{"name":"","id":18}}]},"async":false},{"name":"get_second_untyped","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"first","type":{"name":"","id":18}},{"name":"second","type":{"name":"","id":18}}]},"async":false},{"name":"say_sum_ducktyped","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"","id":18}},{"name":"right","type":{"name":"","id":18}}]},"async":false},{"name":"say_string_without_spaces","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"value","type":{"name":"String","id":7}}]},"async":false},{"name":"say_multiply_ducktyped","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"left","type":{"name":"","id":18}},{"name":"right","type":{"name":"","id":18}}]},"async":false}],"classes":[],"objects":[]}}],"cs":[{"name":"hello.cs","scope":{"name":"global_namespace","funcs":[{"name":"Sum","signature":{"ret":{"type":{"name":"int","id":3}},"args":[{"name":"a","type":{"name":"int","id":3}},{"name":"b","type":{"name":"int","id":3}}]},"async":false},{"name":"Say","signature":{"ret":{"type":{"name":"","id":18}},"args":[{"name":"text","type":{"name":"string","id":7}}]},"async":false},{"name":"Concat","signature":{"ret":{"type":{"name":"string","id":7}},"args":[{"name":"a","type":{"name":"string","id":7}},{"name":"b","type":{"name":"string","id":7}}]},"async":false},{"name":"SayHello","signature":{"ret":{"type":{"name":"","id":18}},"args":[]},"async":false}],"classes":[],"objects":[]}}],"__metacall_host__":[],"mock":[{"name":"empty.mock","scope":{"name":"global_namespace","funcs":[{"name":"three_str","signature":{"ret":{"type":{"name":"String","id":7}},"args":[{"name":"a_str","type":{"name":"String","id":7}},{"name":"b_str","type":{"name":"String","id":7}},{"name":"c_str","type":{"name":"String","id":7}}]},"async":false},{"name":"my_empty_func_str","signature":{"ret":{"type":{"name":"String","id":7}},"args":[]},"async":false},{"name":"my_empty_func_int","signature":{"ret":{"type":{"name":"Integer","id":3}},"args":[]},"async":false},{"name":"new_args","signature":{"ret":{"type":{"name":"String","id":7}},"args":[{"name":"a_str","type":{"name":"String","id":7}}]},"async":false},{"name":"two_str","signature":{"ret":{"type":{"name":"String","id":7}},"args":[{"name":"a_str","type":{"name":"String","id":7}},{"name":"b_str","type":{"name":"String","id":7}}]},"async":false},{"name":"two_doubles","signature":{"ret":{"type":{"name":"Double","id":6}},"args":[{"name":"first_parameter","type":{"name":"Double","id":6}},{"name":"second_parameter","type":{"name":"Double","id":6}}]},"async":false},{"name":"my_empty_func","signature":{"ret":{"type":{"name":"Integer","id":3}},"args":[]},"async":false},{"name":"mixed_args","signature":{"ret":{"type":{"name":"Char","id":1}},"args":[{"name":"a_char","type":{"name":"Char","id":1}},{"name":"b_int","type":{"name":"Integer","id":3}},{"name":"c_long","type":{"name":"Long","id":4}},{"name":"d_double","type":{"name":"Double","id":6}},{"name":"e_ptr","type":{"name":"Ptr","id":11}}]},"async":false}],"classes":[],"objects":[]}}]}'; + res.setHeader('Content-Type', 'application/json'); + res.end(inspect); + return; + } + } else if (req.method === 'POST') { + if (req.url === '/viferga/example/v1/call/divide') { + data.then((body) => { + console.log('¡Call recieved!'); + if (body !== '[50.0,10.0]') { + console.error('Invalid body:', body); + process.exit(1); + } + const result = '5.0'; + res.setHeader('Content-Type', 'application/json'); + res.end(result); + setTimeout(() => { + process.exit(0); + }, 1000); + }); + return; + } + } + + console.error('Invalid request method or url:', req.method, req.url); + process.exit(1); +}); + +server.listen(port, () => { + console.log(`MetaCall server listening at ${port}`); +}); diff --git a/source/tests/metacall_rpc_test/source/test.js b/source/tests/metacall_rpc_test/source/test.js new file mode 100644 index 0000000000..15d5a9dac5 --- /dev/null +++ b/source/tests/metacall_rpc_test/source/test.js @@ -0,0 +1,86 @@ +const { spawn } = require('child_process'); +const http = require('http'); + +// Start mock server +const server = spawn(process.argv[0], [process.argv[2]]); + +server.stdout.pipe(process.stdout); +server.stderr.pipe(process.stderr); + +server.on('exit', (code) => { + if (code !== 0) { + process.exit(code); + } +}); + +// Check if server is ready +function isReady() { + return new Promise((resolve, reject) => { + const options = { + host: 'localhost', + port: 6094, + path: '/ready', + }; + + const callback = (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + resolve(data === 'OK'); + }); + + res.on('error', reject); + }; + try { + const req = http.request(options, callback); + req.on('error', reject); + req.end(); + } catch (e) { + reject(e); + } + }); +} + +// Catch unhandled exceptions +function killTest(error) { + server.kill('SIGINT'); + console.error(error); + process.exit(1); +} + +process.on('uncaughtException', killTest); + +// Wait server to be ready and execute the test +(async function run() { + let ready = false; + + setTimeout(() => { + if (ready === false) { + killTest('Timeout reached, server is not ready'); + } + }, 60000); + + while (ready !== true) { + try { + ready = await isReady(); + } catch (e) { } + } + + console.log('Starting the test'); + + const test = spawn(process.argv[3]); + + test.stdout.pipe(process.stdout); + test.stderr.pipe(process.stderr); + + test.on('exit', (code) => { + if (code !== 0) { + killTest(`Error: Test exited with code ${code}`); + } + process.exit(0); + }); +})(); diff --git a/source/tests/metacall_ruby_fail_empty_test/CMakeLists.txt b/source/tests/metacall_ruby_fail_empty_test/CMakeLists.txt new file mode 100644 index 0000000000..a480292e1c --- /dev/null +++ b/source/tests/metacall_ruby_fail_empty_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-fail-empty-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_fail_empty_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_fail_empty_test/source/main.cpp b/source/tests/metacall_ruby_fail_empty_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_fail_empty_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_fail_empty_test/source/metacall_ruby_fail_empty_test.cpp b/source/tests/metacall_ruby_fail_empty_test/source/metacall_ruby_fail_empty_test.cpp new file mode 100644 index 0000000000..5ab21e63e2 --- /dev/null +++ b/source/tests/metacall_ruby_fail_empty_test/source/metacall_ruby_fail_empty_test.cpp @@ -0,0 +1,73 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_fail_empty_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_fail_empty_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* File */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *scripts[] = { + "failempty.rb" + }; + + const size_t size = sizeof(scripts) / sizeof(scripts[0]); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", scripts, size, NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_fail_test/CMakeLists.txt b/source/tests/metacall_ruby_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..a34d4318cd --- /dev/null +++ b/source/tests/metacall_ruby_fail_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_fail_test/source/main.cpp b/source/tests/metacall_ruby_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp b/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp new file mode 100644 index 0000000000..24c11d7981 --- /dev/null +++ b/source/tests/metacall_ruby_fail_test/source/metacall_ruby_fail_test.cpp @@ -0,0 +1,54 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + metacall_log_stdio_type log_stdio = { stdout }; + + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "invalid.rb" + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_object_class_test/CMakeLists.txt b/source/tests/metacall_ruby_object_class_test/CMakeLists.txt new file mode 100644 index 0000000000..c4d2ad9b38 --- /dev/null +++ b/source/tests/metacall_ruby_object_class_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-object-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_object_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_object_class_test/source/main.cpp b/source/tests/metacall_ruby_object_class_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_object_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_object_class_test/source/metacall_ruby_object_class_test.cpp b/source/tests/metacall_ruby_object_class_test/source/metacall_ruby_object_class_test.cpp new file mode 100644 index 0000000000..05d7f272cb --- /dev/null +++ b/source/tests/metacall_ruby_object_class_test/source/metacall_ruby_object_class_test.cpp @@ -0,0 +1,164 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_object_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_object_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "klass.rb" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + { + void *ret = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(ret)); + void *cls = metacall_value_to_class(ret); + + void *static_var = metacall_class_static_get(cls, "@@class_hierarchy_var"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(static_var)); + metacall_value_destroy(static_var); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(ret)); + void *obj = metacall_value_to_object(ret); + + void *v_int = metacall_value_create_int(1234); + int retcode = metacall_object_set(obj, "@intAttribute", v_int); + ASSERT_EQ((int)0, (int)retcode); + metacall_value_destroy(v_int); + + void *intAttribute = metacall_object_get(obj, "@intAttribute"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(intAttribute)); + ASSERT_EQ((int)1234, (int)metacall_value_to_int(intAttribute)); + metacall_value_destroy(intAttribute); + + void *param3 = metacall_object_get(obj, "@param3"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param3)); + ASSERT_EQ((int)777, (int)metacall_value_to_int(param3)); + metacall_value_destroy(param3); + + metacall_value_destroy(ret); + } + + { + void *myclass = metacall_class("MyClass"); + ASSERT_NE((void *)NULL, (void *)myclass); + + static const char works[] = "It works!"; + + void *static_method_args[] = { + metacall_value_create_string(works, sizeof(works) - 1) + }; + void *ret_value = metacallv_class(myclass, "static_hello", static_method_args, sizeof(static_method_args) / sizeof(static_method_args[0])); + metacall_value_destroy(static_method_args[0]); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret_value)); + metacall_value_destroy(ret_value); + } + + { + void *obj_value = metacall("return_object_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_OBJECT, (enum metacall_value_id)metacall_value_id(obj_value)); + void *obj = metacall_value_to_object(obj_value); + + static const char world[] = "world"; + + void *return_bye_args[] = { + metacall_value_create_string(world, sizeof(world) - 1) + }; + void *ret = metacallv_object(obj, "return_bye", return_bye_args, sizeof(return_bye_args) / sizeof(return_bye_args[0])); + + ASSERT_EQ((enum metacall_value_id)METACALL_STRING, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + metacall_value_destroy(obj_value); + metacall_value_destroy(return_bye_args[0]); + } + + { + void *myclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(myclass_value)); + void *myclass = metacall_value_to_class(myclass_value); + + static const char john[] = "John Doe"; + + void *constructor_params[] = { + metacall_value_create_string(john, sizeof(john) - 1), // param1 + metacall_value_create_int(999999) // param2 + }; + void *new_object_v = metacall_class_new(myclass, "objectname", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + metacall_value_destroy(constructor_params[1]); + void *new_object = metacall_value_to_object(new_object_v); + + void *param2 = metacall_object_get(new_object, "@param2"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)999999, (int)metacall_value_to_int(param2)); + + metacall_value_destroy(param2); + metacall_value_destroy(myclass_value); + metacall_value_destroy(new_object_v); + } + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_parser_integration_test/CMakeLists.txt b/source/tests/metacall_ruby_parser_integration_test/CMakeLists.txt new file mode 100644 index 0000000000..ecd892ffb0 --- /dev/null +++ b/source/tests/metacall_ruby_parser_integration_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-parser-integration-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_parser_integration_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_parser_integration_test/source/main.cpp b/source/tests/metacall_ruby_parser_integration_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_parser_integration_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_parser_integration_test/source/metacall_ruby_parser_integration_test.cpp b/source/tests/metacall_ruby_parser_integration_test/source/metacall_ruby_parser_integration_test.cpp new file mode 100644 index 0000000000..0e38e65ac9 --- /dev/null +++ b/source/tests/metacall_ruby_parser_integration_test/source/metacall_ruby_parser_integration_test.cpp @@ -0,0 +1,84 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_ruby_parser_integration_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_parser_integration_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "cache.rb" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + ret = metacall("cache_initialize"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + ret = metacall("cache_set", "meta", "call"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); + + ret = metacall("cache_has_key", "meta"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_NE((int)0, (int)metacall_value_to_bool(ret)); + + metacall_value_destroy(ret); + + ret = metacall("cache_get", "meta"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "call"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_rails_integration_test/CMakeLists.txt b/source/tests/metacall_ruby_rails_integration_test/CMakeLists.txt new file mode 100644 index 0000000000..6559f627a6 --- /dev/null +++ b/source/tests/metacall_ruby_rails_integration_test/CMakeLists.txt @@ -0,0 +1,165 @@ +# Check if ruby loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-rails-integration-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_rails_integration_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Dependecies +# + +add_dependencies(${target} + ${META_PROJECT_NAME}::metacall +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH} +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_rails_integration_test/source/main.cpp b/source/tests/metacall_ruby_rails_integration_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_ruby_rails_integration_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_rails_integration_test/source/metacall_ruby_rails_integration_test.cpp b/source/tests/metacall_ruby_rails_integration_test/source/metacall_ruby_rails_integration_test.cpp new file mode 100644 index 0000000000..5d04ac4351 --- /dev/null +++ b/source/tests/metacall_ruby_rails_integration_test/source/metacall_ruby_rails_integration_test.cpp @@ -0,0 +1,63 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_ruby_integration_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_ruby_integration_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) + { + const char *rb_scripts[] = { + "blog.rb" + }; + + const char func_run[] = "run_and_kill_server"; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + EXPECT_NE((void *)NULL, (void *)metacall_function(func_run)); + + ret = metacall(func_run); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((int)0, (int)metacall_value_to_int(ret)); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_RB */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_ruby_test/CMakeLists.txt b/source/tests/metacall_ruby_test/CMakeLists.txt new file mode 100644 index 0000000000..163c170a64 --- /dev/null +++ b/source/tests/metacall_ruby_test/CMakeLists.txt @@ -0,0 +1,156 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-ruby-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_ruby_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_ruby_test/source/main.cpp b/source/tests/metacall_ruby_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_ruby_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_ruby_test/source/metacall_ruby_test.cpp b/source/tests/metacall_ruby_test/source/metacall_ruby_test.cpp new file mode 100644 index 0000000000..cf8bcf49ea --- /dev/null +++ b/source/tests/metacall_ruby_test/source/metacall_ruby_test.cpp @@ -0,0 +1,41 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_ruby_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_ruby_test, DefaultConstructor) +{ + const char *rb_scripts[] = { + "hello.rb" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_class_test/CMakeLists.txt b/source/tests/metacall_rust_class_test/CMakeLists.txt new file mode 100644 index 0000000000..28a7f1dac5 --- /dev/null +++ b/source/tests/metacall_rust_class_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_class_test/source/main.cpp b/source/tests/metacall_rust_class_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_class_test/source/metacall_rust_class_test.cpp b/source/tests/metacall_rust_class_test/source/metacall_rust_class_test.cpp new file mode 100644 index 0000000000..235841a990 --- /dev/null +++ b/source/tests/metacall_rust_class_test/source/metacall_rust_class_test.cpp @@ -0,0 +1,107 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_class_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_class_test, DefaultConstructor) +{ + const char *rs_scripts[] = { + "class.rs" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rs", rs_scripts, sizeof(rs_scripts) / sizeof(rs_scripts[0]), NULL)); + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *ret_value = metacallv_class(book_class, "get_number", nullptr, 0); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret_value)); + ASSERT_EQ((int)123, (int)metacall_value_to_int(ret_value)); + metacall_value_destroy(ret_value); + // metacall_value_destroy(book_class); + } + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *constructor_params[] = { + metacall_value_create_int(111) // param1 + }; + void *new_object_v = metacall_class_new(book_class, "book_one", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + void *new_object = metacall_value_to_object(new_object_v); + + void *ret = metacallv_object(new_object, "get_price", nullptr, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(ret)); + + void *param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(param2)); + + metacall_value_destroy(param2); + + void *int_value = metacall_value_create_int(100); + int retcode = metacall_object_set(new_object, "price", int_value); + metacall_value_destroy(int_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)100, (int)metacall_value_to_int(param2)); + metacall_value_destroy(param2); + + metacall_value_destroy(new_object_v); + metacall_value_destroy(ret); + // metacall_value_destroy(myclass_value); + // metacall_value_destroy(book_class); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_mem_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_mem_test/CMakeLists.txt new file mode 100644 index 0000000000..026115ba3b --- /dev/null +++ b/source/tests/metacall_rust_load_from_mem_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-memory-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_mem_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_mem_test/source/main.cpp b/source/tests/metacall_rust_load_from_mem_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_mem_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_mem_test/source/metacall_rust_load_from_mem_test.cpp b/source/tests/metacall_rust_load_from_mem_test/source/metacall_rust_load_from_mem_test.cpp new file mode 100644 index 0000000000..ab09ed72b5 --- /dev/null +++ b/source/tests/metacall_rust_load_from_mem_test/source/metacall_rust_load_from_mem_test.cpp @@ -0,0 +1,73 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_mem_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_mem_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + // Test: Load from memory + static const char buffer[] = + "fn add(num_1: i32, num_2: i32) -> i32 {\n" + "\tnum_1 + num_2\n" + "}" + "fn add2(num_1: f32, num_2: f32) -> f32 {\n" + "\tnum_1 + num_2\n" + "}"; + + EXPECT_EQ((int)0, (int)metacall_load_from_memory("rs", buffer, sizeof(buffer), NULL)); + void *ret = metacall("add", 5, 10); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + ret = metacall("add2", 5.0, 10.0); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(ret); + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_package_class_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_package_class_test/CMakeLists.txt new file mode 100644 index 0000000000..0f98b613a9 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_class_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-package-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_package_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_package_class_test/source/main.cpp b/source/tests/metacall_rust_load_from_package_class_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_package_class_test/source/metacall_rust_load_from_package_class_test.cpp b/source/tests/metacall_rust_load_from_package_class_test/source/metacall_rust_load_from_package_class_test.cpp new file mode 100644 index 0000000000..d4f927d5b2 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_class_test/source/metacall_rust_load_from_package_class_test.cpp @@ -0,0 +1,106 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_mem_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_mem_test, DefaultConstructor) +{ + const char *rs_script = "libclass.rlib"; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_package("rs", rs_script, NULL)); + + // Test: Load from package + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *ret_value = metacallv_class(book_class, "get_number", nullptr, 0); + + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret_value)); + ASSERT_EQ((int)123, (int)metacall_value_to_int(ret_value)); + metacall_value_destroy(ret_value); + } + { + void *book_class = metacall_class("Book"); + ASSERT_NE((void *)NULL, (void *)book_class); + + void *constructor_params[] = { + metacall_value_create_int(111) // param1 + }; + void *new_object_v = metacall_class_new(book_class, "book_one", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + metacall_value_destroy(constructor_params[0]); + void *new_object = metacall_value_to_object(new_object_v); + + void *ret = metacallv_object(new_object, "get_price", nullptr, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(ret)); + + void *param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)111, (int)metacall_value_to_int(param2)); + + metacall_value_destroy(param2); + + void *int_value = metacall_value_create_int(100); + int retcode = metacall_object_set(new_object, "price", int_value); + metacall_value_destroy(int_value); + ASSERT_EQ((int)0, int(retcode)); + + param2 = metacall_object_get(new_object, "price"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((int)100, (int)metacall_value_to_int(param2)); + metacall_value_destroy(param2); + + metacall_value_destroy(new_object_v); + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_package_dep_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_package_dep_test/CMakeLists.txt new file mode 100644 index 0000000000..7ac9429cf3 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_dep_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-package-dep-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_package_dep_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_package_dep_test/source/main.cpp b/source/tests/metacall_rust_load_from_package_dep_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_dep_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_package_dep_test/source/metacall_rust_load_from_package_dep_test.cpp b/source/tests/metacall_rust_load_from_package_dep_test/source/metacall_rust_load_from_package_dep_test.cpp new file mode 100644 index 0000000000..565883bc28 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_dep_test/source/metacall_rust_load_from_package_dep_test.cpp @@ -0,0 +1,69 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_package_dep_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_package_dep_test, DefaultConstructor) +{ + const char *rs_script = "debug/libjson_wrapper.rlib"; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_package("rs", rs_script, NULL)); + + /* Test: Load from package */ + { + const char *text = "{\"name\": \"John Doe\"}"; + void *ret = metacall("compile", text); + ASSERT_NE((void *)NULL, (void *)ret); + EXPECT_STREQ(metacall_value_to_string(ret), "\"John Doe\""); + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_load_from_package_test/CMakeLists.txt b/source/tests/metacall_rust_load_from_package_test/CMakeLists.txt new file mode 100644 index 0000000000..a5aaabbe66 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-load-from-package-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_load_from_package_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_load_from_package_test/source/main.cpp b/source/tests/metacall_rust_load_from_package_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_load_from_package_test/source/metacall_rust_load_from_package_test.cpp b/source/tests/metacall_rust_load_from_package_test/source/metacall_rust_load_from_package_test.cpp new file mode 100644 index 0000000000..cfb3b49957 --- /dev/null +++ b/source/tests/metacall_rust_load_from_package_test/source/metacall_rust_load_from_package_test.cpp @@ -0,0 +1,172 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_load_from_mem_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_load_from_mem_test, DefaultConstructor) +{ + const char *rs_script = "libbasic.rlib"; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_package("rs", rs_script, NULL)); + // Test: Load from package + + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_int(3); + array_value[1] = metacall_value_create_int(5); + array_value[2] = metacall_value_create_int(7); + + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + // void *ret = metacallv_s("add_vec", array_args, 1); + // EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_float(3.0); + array_value[1] = metacall_value_create_float(5.0); + array_value[2] = metacall_value_create_float(7.0); + + void *ret = metacallv_s("add_float_vec", array_args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add", 5, 10); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("run"); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add_float", 5.0, 10.0); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(ret); + } + + // { + // void *ret = metacall("string_len", "Test String"); + // EXPECT_EQ((long)11, (long)metacall_value_to_long(ret)); + // ret = metacall("new_string", 123); + // EXPECT_STREQ(metacall_value_to_string(ret), "get number 123"); + // metacall_value_destroy(ret); + // } + + { + // test if we can return vec + void *ret_vec = metacall("return_vec"); + void *array_args[] = { + ret_vec + }; + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret_vec); + metacall_value_destroy(ret); + } + + { + void *args[] = { + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + void **tuple0 = metacall_value_to_array(map_value[0]); + static const int key0 = 3; + tuple0[0] = metacall_value_create_int(key0); + tuple0[1] = metacall_value_create_float(5.0); + + map_value[1] = metacall_value_create_array(NULL, 2); + void **tuple1 = metacall_value_to_array(map_value[1]); + static const int key1 = 5; + tuple1[0] = metacall_value_create_int(key1); + tuple1[1] = metacall_value_create_float(10.0); + + void *ret = metacallv_s("add_map", args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(args[0]); + metacall_value_destroy(ret); + } + + { + // test if we can return map + void *ret = metacall("return_map"); + void **map_value2 = metacall_value_to_map(ret); + void **tuple0 = metacall_value_to_array(map_value2[0]); + EXPECT_EQ((int)metacall_value_to_int(tuple0[0]), (int)metacall_value_to_float(tuple0[1])); + void **tuple1 = metacall_value_to_array(map_value2[1]); + EXPECT_EQ((int)metacall_value_to_int(tuple1[0]), (int)metacall_value_to_float(tuple1[1])); + void **tuple2 = metacall_value_to_array(map_value2[2]); + EXPECT_EQ((int)metacall_value_to_int(tuple2[0]), (int)metacall_value_to_float(tuple2[1])); + metacall_value_destroy(ret); + } + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_rust_test/CMakeLists.txt b/source/tests/metacall_rust_test/CMakeLists.txt new file mode 100644 index 0000000000..31434b1be3 --- /dev/null +++ b/source/tests/metacall_rust_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-rust-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_rust_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + rs_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} + + # Enable Rust backtrace and logs for better debugging + RUST_BACKTRACE=1 + RUST_LOG=INFO +) diff --git a/source/tests/metacall_rust_test/source/main.cpp b/source/tests/metacall_rust_test/source/main.cpp new file mode 100644 index 0000000000..37d4adc23f --- /dev/null +++ b/source/tests/metacall_rust_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_rust_test/source/metacall_rust_test.cpp b/source/tests/metacall_rust_test/source/metacall_rust_test.cpp new file mode 100644 index 0000000000..55736c73e2 --- /dev/null +++ b/source/tests/metacall_rust_test/source/metacall_rust_test.cpp @@ -0,0 +1,191 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_rust_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_rust_test, DefaultConstructor) +{ + const char *rs_scripts[] = { + "basic.rs" + }; + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + EXPECT_EQ((int)0, (int)metacall_load_from_file("rs", rs_scripts, sizeof(rs_scripts) / sizeof(rs_scripts[0]), NULL)); + + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_int(3); + array_value[1] = metacall_value_create_int(5); + array_value[2] = metacall_value_create_int(7); + + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + // void *ret = metacallv_s("add_vec", array_args, 1); + // EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + { + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_float(3.0); + array_value[1] = metacall_value_create_float(5.0); + array_value[2] = metacall_value_create_float(7.0); + + void *ret = metacallv_s("add_float_vec", array_args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(array_args[0]); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add", 5, 10); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("run"); + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + metacall_value_destroy(ret); + } + + { + void *ret = metacall("add_float", 5.0, 10.0); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(ret); + } + + // { + // void *ret = metacall("string_len", "Test String"); + // EXPECT_EQ((long)11, (long)metacall_value_to_long(ret)); + // ret = metacall("new_string", 123); + // EXPECT_STREQ(metacall_value_to_string(ret), "get number 123"); + // metacall_value_destroy(ret); + // } + + { + void *ret = metacall("str_slice", "hellow"); + EXPECT_STREQ(metacall_value_to_string(ret), "hel"); + metacall_value_destroy(ret); + } + + { + // test if we can return vec + void *ret_vec = metacall("return_vec"); + void *array_args[] = { + ret_vec + }; + void *ret = metacallv_s("add_vec2", array_args, 1); + EXPECT_EQ((int)15, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret_vec); + metacall_value_destroy(ret); + } + + { + void *args[] = { + metacall_value_create_map(NULL, 2) + }; + + void **map_value = metacall_value_to_map(args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + void **tuple0 = metacall_value_to_array(map_value[0]); + static const int key0 = 3; + tuple0[0] = metacall_value_create_int(key0); + tuple0[1] = metacall_value_create_float(5.0); + + map_value[1] = metacall_value_create_array(NULL, 2); + void **tuple1 = metacall_value_to_array(map_value[1]); + static const int key1 = 5; + tuple1[0] = metacall_value_create_int(key1); + tuple1[1] = metacall_value_create_float(10.0); + + void *ret = metacallv_s("add_map", args, 1); + EXPECT_EQ((float)15.0, (float)metacall_value_to_float(ret)); + metacall_value_destroy(args[0]); + metacall_value_destroy(ret); + } + + { + // test if we can return map + void *ret = metacall("return_map"); + void **map_value2 = metacall_value_to_map(ret); + void **tuple0 = metacall_value_to_array(map_value2[0]); + EXPECT_EQ((int)metacall_value_to_int(tuple0[0]), (int)metacall_value_to_float(tuple0[1])); + void **tuple1 = metacall_value_to_array(map_value2[1]); + EXPECT_EQ((int)metacall_value_to_int(tuple1[0]), (int)metacall_value_to_float(tuple1[1])); + void **tuple2 = metacall_value_to_array(map_value2[2]); + EXPECT_EQ((int)metacall_value_to_int(tuple2[0]), (int)metacall_value_to_float(tuple2[1])); + metacall_value_destroy(ret); + } + + /* TODO */ + /* + { + // test if we can return result + void *ret = metacall("age_group", 21); + + // TODO + + metacall_value_destroy(ret); + } + */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_sandbox_plugin_test/CMakeLists.txt b/source/tests/metacall_sandbox_plugin_test/CMakeLists.txt new file mode 100644 index 0000000000..3a98235b10 --- /dev/null +++ b/source/tests/metacall_sandbox_plugin_test/CMakeLists.txt @@ -0,0 +1,175 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS OR NOT OPTION_BUILD_PLUGINS_SANDBOX) + return() +endif() + +include(Portability) + +# Sandbox is only supported in Linux +if(NOT PROJECT_OS_FAMILY STREQUAL unix) + return() +endif() + +# Thread Sanitizer deadlocks with ASSERT_EXIT (TODO: Review this when fork safety is implemented in sandbox_plugin) +if(OPTION_BUILD_THREAD_SANITIZER) + return() +endif() + +# TODO: Test if we can make it run properly with use_sigaltstack=1 (https://groups.google.com/a/chromium.org/g/chromium-reviews/c/xX4xDy21714/m/1yKX1JLE620J) +if(OPTION_BUILD_ADDRESS_SANITIZER) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-sandbox-plugin-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_sandbox_plugin_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + ext_loader + sandbox_plugin +) + +# +# Define test properties +# + +set_property(TEST ${target} + # Valgrind does not work well with libseccomp + PROPERTY LABELS ${target} MEMCHECK_IGNORE +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_sandbox_plugin_test/source/main.cpp b/source/tests/metacall_sandbox_plugin_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_sandbox_plugin_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_sandbox_plugin_test/source/metacall_sandbox_plugin_test.cpp b/source/tests/metacall_sandbox_plugin_test/source/metacall_sandbox_plugin_test.cpp new file mode 100644 index 0000000000..3cd32dcf0f --- /dev/null +++ b/source/tests/metacall_sandbox_plugin_test/source/metacall_sandbox_plugin_test.cpp @@ -0,0 +1,652 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +void invalid_syscall(void) +{ + struct utsname data; + uname(&data); + printf("%s\n", data.sysname); +} + +void invalid_io_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable io syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_io", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + printf("Invalid IO syscall\n"); +} + +#include + +void invalid_sockets_syscall() +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + close(fd); +} + +#include +#include + +void invalid_ipc_syscall() +{ + // Create a shared memory segment + int shm_id = shmget(1234, 1024, IPC_CREAT | 0666); + if (shm_id == -1) + { + perror("shmget"); + exit(EXIT_FAILURE); + } + + // Attach the shared memory segment to the process's address space + void *shm_addr = shmat(shm_id, NULL, 0); + if (shm_addr == (void *)-1) + { + perror("shmat"); + exit(EXIT_FAILURE); + } + + // Write data to shared memory + const char *message = "Hello, Shared Memory!"; + strncpy((char *)shm_addr, message, 1024); + + printf("Data written to shared memory: %s\n", (char *)shm_addr); + + // Detach the shared memory segment + if (shmdt(shm_addr) == -1) + { + perror("shmdt"); + exit(EXIT_FAILURE); + } + + // Remove the shared memory segment + if (shmctl(shm_id, IPC_RMID, NULL) == -1) + { + perror("shmctl"); + exit(EXIT_FAILURE); + } +} + +#include +#include + +void invalid_process_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable process syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_process", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + pid_t pid = fork(); + + if (pid < 0) + { + perror("fork"); + exit(EXIT_FAILURE); + } + + if (pid == 0) + { + // Child process + exit(EXIT_SUCCESS); // Exit immediately + } + else + { + // Parent process + int status; + if (waitpid(pid, &status, 0) == -1) + { + perror("waitpid"); + exit(EXIT_FAILURE); + } + } +} + +#include +#include + +void invalid_filesystems_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable filesystems syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_filesystems", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + int fd = open("/tmp/testfile", O_RDONLY); + if (fd == -1) + { + perror("open"); + exit(EXIT_FAILURE); + } + + close(fd); +} + +#include + +void invalid_time_syscall() +{ + sleep(1); +} + +#include + +void invalid_memory_syscall(void *sandbox_ctx, void *handle) +{ + /* Disable memory syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_memory", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) + { + perror("mmap"); + exit(EXIT_FAILURE); + } + munmap(addr, 4096); +} + +#include + +void invalid_signals_syscall() +{ + if (signal(SIGINT, SIG_IGN) == SIG_ERR) + { + perror("signal"); + exit(EXIT_FAILURE); + } +} +class metacall_sandbox_plugin_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_sandbox_plugin_test, DefaultConstructor) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable uname syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_uname", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute uname */ + ASSERT_EXIT({ invalid_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + /* Testing invalid number of parameters */ + { + void *ret = metacallhv_s(handle, "sandbox_destroy", metacall_null_args, 0); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + } + + /* Testing correct number of parameters but invalid type */ + { + void *args[1] = { metacall_value_create_long(2343) }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((enum metacall_value_id)metacall_value_id(ret), (enum metacall_value_id)METACALL_THROWABLE); + + metacall_value_destroy(ret); + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +/* [Note] This test blocks all the gtest context, so you should comment it to allow testing for other test cases */ +TEST_F(metacall_sandbox_plugin_test, SANDBOX_IO_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute fopen */ + ASSERT_EXIT({ invalid_io_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_SOCKETS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable sockets syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_sockets", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute socket */ + ASSERT_EXIT({ invalid_sockets_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_IPC_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable IPC syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_ipc", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute IPC operation */ + ASSERT_EXIT({ invalid_ipc_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_PROCESS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute process operation */ + ASSERT_EXIT({ invalid_process_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_FILESYSTEMS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute file systems operation */ + ASSERT_EXIT({ invalid_filesystems_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_TIME_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable Time syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_time", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute time operation */ + ASSERT_EXIT({ invalid_time_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_MEMORY_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Generate a syscall exception when trying to execute memory operation */ + ASSERT_EXIT({ invalid_memory_syscall(sandbox_ctx, handle); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} + +TEST_F(metacall_sandbox_plugin_test, SANDBOX_SIGNALS_DISABLE_TEST) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + void *sandbox_ctx, *handle = metacall_plugin_core(); + + ASSERT_NE((void *)NULL, (void *)handle); + + /* Initialize sandboxing */ + { + void *args[1] = { metacall_value_create_bool(1L) /* Allow */ }; + + sandbox_ctx = metacallhv_s(handle, "sandbox_initialize", args, 1); + + EXPECT_NE((void *)NULL, (void *)sandbox_ctx); + EXPECT_NE((void *)metacall_value_to_ptr(sandbox_ctx), (void *)NULL); + + metacall_value_destroy(args[0]); + } + + /* Disable IPC syscall */ + { + void *args[2] = { sandbox_ctx, metacall_value_create_bool(0L) /* Kill */ }; + + void *ret = metacallhv_s(handle, "sandbox_signals", args, 2); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(ret); + metacall_value_destroy(args[1]); + } + + /* Generate a syscall exception when trying to execute signals operation */ + ASSERT_EXIT({ invalid_signals_syscall(); }, testing::KilledBySignal(SIGSYS), ""); + + /* Destroy sandboxing */ + { + void *args[1] = { sandbox_ctx }; + + void *ret = metacallhv_s(handle, "sandbox_destroy", args, 1); + + EXPECT_NE((void *)NULL, (void *)ret); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)0); + + metacall_value_destroy(args[0]); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_test/CMakeLists.txt b/source/tests/metacall_test/CMakeLists.txt index dcfc4b1e1a..8751e1fe0e 100644 --- a/source/tests/metacall_test/CMakeLists.txt +++ b/source/tests/metacall_test/CMakeLists.txt @@ -80,19 +80,6 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader ${META_PROJECT_NAME}::metacall ) @@ -114,11 +101,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -127,10 +123,47 @@ target_link_libraries(${target} # Define test # +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: data race (pid=13427) + # Write of size 8 at 0x7b5c00010680 by thread T8: + # #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:706 (libtsan.so.2+0x47e82) + # #1 (libcoreclr.so+0x36ba88) + # + # Previous write of size 8 at 0x7b5c00010680 by main thread: + # [failed to restore the stack] + # + # Thread T8 '.NET ThreadPool' (tid=13563, running) created by thread T7 at: + # #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1001 (libtsan.so.2+0x5e686) + # #1 (libcoreclr.so+0x4e90ce) + # + # SUMMARY: ThreadSanitizer: data race (/usr/share/dotnet/shared/Microsoft.NETCore.App/5.0.17/libcoreclr.so+0x36ba88) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_loader_dependencies(${target} + node_loader + py_loader + rb_loader + cs_loader + jsm_loader + js_loader + mock_loader + c_loader + file_loader +) + # # Define test properties # @@ -139,6 +172,20 @@ set_property(TEST ${target} PROPERTY LABELS ${target} MEMCHECK_IGNORE ) +if(OPTION_BUILD_ADDRESS_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with sanitizer (this happens when C# loader is enabled): + # + # Tracer caught signal 11: addr=0x600000690 pc=0x7f3a7b6710f0 sp=0x7f3a75e32d10 + # LeakSanitizer has encountered a fatal error. + # HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1 + # HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + include(TestEnvironmentVariables) test_environment_variables(${target} diff --git a/source/tests/metacall_test/source/main.cpp b/source/tests/metacall_test/source/main.cpp index 0cbc70d36b..5820341294 100644 --- a/source/tests/metacall_test/source/main.cpp +++ b/source/tests/metacall_test/source/main.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/metacall_test/source/metacall_test.cpp b/source/tests/metacall_test/source/metacall_test.cpp index 3fd5970100..ddbf10c03b 100644 --- a/source/tests/metacall_test/source/metacall_test.cpp +++ b/source/tests/metacall_test/source/metacall_test.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,38 @@ * */ -#include +#include #include -#include #include +#include + +#include + +#if defined(WIN32) || defined(_WIN32) + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif -void * c_function(void * args[]) + #include +#elif defined(unix) || defined(__unix__) || defined(__unix) || \ + defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ + defined(__CYGWIN__) || defined(__CYGWIN32__) || \ + defined(__MINGW32__) || defined(__MINGW64__) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #include +#endif + +void *c_function(size_t argc, void *args[], void *data) { - printf("%s\n", (char*)args[0]); + (void)argc; + (void)data; + + printf("%s\n", (char *)args[0]); return metacall_value_create_int(1); } @@ -42,50 +65,76 @@ TEST_F(metacall_test, DefaultConstructor) metacall_log_stdio_type log_stdio = { stdout }; - ASSERT_EQ((int) 0, (int) metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + ASSERT_EQ((int)0, (int)metacall_log(METACALL_LOG_STDIO, (void *)&log_stdio)); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + #if defined(WIN32) || defined(_WIN32) + + DWORD length = MAX_PATH; + char cwd[MAX_PATH]; + + ASSERT_NE((DWORD)0, (DWORD)GetCurrentDirectory(length, cwd)); + + #elif defined(unix) || defined(__unix__) || defined(__unix) || \ + defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) || \ + defined(__CYGWIN__) || defined(__CYGWIN32__) || \ + defined(__MINGW32__) || defined(__MINGW64__) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + + char cwd[PATH_MAX]; - ASSERT_EQ((int) 0, (int) metacall_initialize()); + ASSERT_NE((char *)NULL, (char *)getcwd(cwd, sizeof(cwd))); + + #endif + + /* Lazy evaluation of execution paths (do not initialize Python runtime) */ + ASSERT_EQ((int)0, (int)metacall_execution_path("py", cwd)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ /* Native register */ { - void * ret = NULL; + void *ret = NULL; - void * func = NULL; + void *func = NULL; int result = 0; - metacall_register("c_print", c_function, METACALL_INT, 1, METACALL_STRING); + ASSERT_EQ((int)0, (int)metacall_register("c_print", c_function, NULL, METACALL_INT, 1, METACALL_STRING)); ret = metacall("c_print", "Hello native function!"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); result = metacall_value_to_int(ret); - EXPECT_EQ((int) result, (int) 1); + EXPECT_EQ((int)result, (int)1); metacall_value_destroy(ret); func = metacall_function("c_print"); - EXPECT_NE((void *) NULL, (void *) func); + EXPECT_NE((void *)NULL, (void *)func); ret = metacallf(func, "Hello native function!"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); result = metacall_value_to_int(ret); - EXPECT_EQ((int) result, (int) 1); + EXPECT_EQ((int)result, (int)1); metacall_value_destroy(ret); } - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) { - const char * py_scripts[] = - { + const char *py_scripts[] = { "example.py", "web.py" }; @@ -103,15 +152,18 @@ TEST_F(metacall_test, DefaultConstructor) long iterator; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + /* Test for asyncness Python introspection */ + EXPECT_EQ((int)0, metacall_function_async(metacall_function("multiply"))); ret = metacall("multiply", 5, 15); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 75); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)75); metacall_value_destroy(ret); @@ -119,80 +171,90 @@ TEST_F(metacall_test, DefaultConstructor) { ret = metacall("multiply", 7, iterator); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)(7 * iterator)); metacall_value_destroy(ret); } ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)32.0); metacall_value_destroy(ret); ret = metacall("sum", 1000, 3500); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 4500); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)4500); metacall_value_destroy(ret); ret = metacall("sum", 3, 4); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((long) metacall_value_to_long(ret), (long) 7); + EXPECT_EQ((long)metacall_value_to_long(ret), (long)7); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *)metacall("hello")); + ret = metacall("hello"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + EXPECT_EQ((void *)NULL, (void *)metacall_value_to_null(ret)); + + metacall_value_destroy(ret); ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello Universe"); metacall_value_destroy(ret); /* Testing Python Buffer */ char buffer[] = { 0, 1, 2, 3, 4 }; - void * buffer_value = metacall_value_create_buffer((void *)buffer, sizeof(buffer)); + void *buffer_value = metacall_value_create_buffer((void *)buffer, sizeof(buffer)); - void * args[] = { + void *args[] = { buffer_value }; - EXPECT_NE((void *) NULL, (void *) buffer_value); + EXPECT_NE((void *)NULL, (void *)buffer_value); ret = metacallv("bytebuff", args); metacall_value_destroy(buffer_value); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((size_t) sizeof("abcd"), (size_t) metacall_value_size(ret)); + EXPECT_EQ((size_t)sizeof("abcd") - 1, (size_t)metacall_value_size(ret)); - EXPECT_EQ((int) 0, (int) memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); + EXPECT_EQ((int)0, (int)memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); metacall_value_destroy(ret); - unsigned char * dyn_buffer = (unsigned char *)malloc(sizeof(unsigned char) * 256); + const size_t dyn_buffer_size = 256; + + unsigned char *dyn_buffer = (unsigned char *)malloc(sizeof(unsigned char) * dyn_buffer_size); - for (int i = 0; i < 256; ++i) + for (size_t i = 0; i < dyn_buffer_size; ++i) { - dyn_buffer[i] = i; + dyn_buffer[i] = (unsigned char)i; } - buffer_value = metacall_value_create_buffer((void *)dyn_buffer, 256); + buffer_value = metacall_value_create_buffer((void *)dyn_buffer, dyn_buffer_size); - EXPECT_NE((void *) NULL, (void *) buffer_value); + EXPECT_NE((void *)NULL, (void *)buffer_value); args[0] = buffer_value; @@ -200,11 +262,11 @@ TEST_F(metacall_test, DefaultConstructor) metacall_value_destroy(buffer_value); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((size_t) sizeof("abcd"), (size_t) metacall_value_size(ret)); + EXPECT_EQ((size_t)sizeof("abcd") - 1, (size_t)metacall_value_size(ret)); - EXPECT_EQ((int) 0, (int) memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); + EXPECT_EQ((int)0, (int)memcmp(metacall_value_to_buffer(ret), "abcd", metacall_value_size(ret))); metacall_value_destroy(ret); @@ -212,232 +274,315 @@ TEST_F(metacall_test, DefaultConstructor) ret = metacall("index"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), web_content)); + EXPECT_STREQ(metacall_value_to_string(ret), web_content); metacall_value_destroy(ret); + + #if !defined(__MEMORYCHECK__) && !defined(__ADDRESS_SANITIZER__) && !defined(__THREAD_SANITIZER__) && !defined(__MEMORY_SANITIZER__) + /* Testing corrupted value input */ + struct + { + int a; + int corrupted; + int value; + char padding[100]; + } corrupted_value = { 0, 0, 0, { 0 } }; + + void *corrupted_args[] = { + (void *)&corrupted_value, + (void *)&corrupted_value + }; + + EXPECT_EQ((void *)NULL, (void *)metacallfv_s(metacall_function("multiply"), corrupted_args, 2)); + + /* Testing freed value input */ + + /* TODO: The next snippet of code works but address sanitizer warns about access warning, + * because in order to check the magic number we need to access the pointer to the struct. + * A better solution would be, when using a custom memory pool allocator (aka object pool) + * detect if the pointer is allocated or not in the object pool, so we track it without + * need to access the pointer in order to read it. This can be a better improvement but + * for now, this would be sufficient to catch most of the errors. + */ + + /* + void *freed_args[] = { + (void *)metacall_value_create_long(3L), + (void *)metacall_value_create_long(5L) + }; + + metacall_value_destroy(freed_args[0]); + metacall_value_destroy(freed_args[1]); + + EXPECT_EQ((void *)NULL, (void *)metacallfv_s(metacall_function("multiply"), freed_args, 2)); + */ + #endif } - #endif /* OPTION_BUILD_LOADERS_PY */ +#endif /* OPTION_BUILD_LOADERS_PY */ - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) +/* Ruby */ +#if defined(OPTION_BUILD_LOADERS_RB) { - const char * rb_scripts[] = - { + const char *rb_scripts[] = { "hello.rb", "second.rb" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); ret = metacall("say_multiply", 5, 7); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 35); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)35); metacall_value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall("say_null")); + ret = metacall("say_null"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + + metacall_value_destroy(ret); ret = metacall("say_hello", "meta-programmer"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello meta-programmer!")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello meta-programmer!"); metacall_value_destroy(ret); ret = metacall("get_second", 5, 12); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 12); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)12); metacall_value_destroy(ret); - const enum metacall_value_id backwards_prime_int_ids[] = - { + const enum metacall_value_id backwards_prime_int_ids[] = { METACALL_INT, METACALL_INT }; ret = metacallt("backwardsPrime", backwards_prime_int_ids, 9900, 10000); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - void ** v_array = metacall_value_to_array(ret); + void **v_array = metacall_value_to_array(ret); - EXPECT_EQ((int) metacall_value_to_int(v_array[0]), (int) 9923); - EXPECT_EQ((int) metacall_value_to_int(v_array[1]), (int) 9931); - EXPECT_EQ((int) metacall_value_to_int(v_array[2]), (int) 9941); - EXPECT_EQ((int) metacall_value_to_int(v_array[3]), (int) 9967); + EXPECT_EQ((int)metacall_value_to_int(v_array[0]), (int)9923); + EXPECT_EQ((int)metacall_value_to_int(v_array[1]), (int)9931); + EXPECT_EQ((int)metacall_value_to_int(v_array[2]), (int)9941); + EXPECT_EQ((int)metacall_value_to_int(v_array[3]), (int)9967); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_RB */ +#endif /* OPTION_BUILD_LOADERS_RB */ - /* JavaScript SpiderMonkey */ - #if defined(OPTION_BUILD_LOADERS_JSM) +/* JavaScript SpiderMonkey */ +#if defined(OPTION_BUILD_LOADERS_JSM) { - const char * jsm_scripts[] = - { + const char *jsm_scripts[] = { "spider.jsm" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("jsm", jsm_scripts, sizeof(jsm_scripts) / sizeof(jsm_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("say_spider", 8, 4)); + EXPECT_EQ((void *)NULL, (void *)metacall("say_spider", 8, 4)); } - #endif /* OPTION_BUILD_LOADERS_JSM */ +#endif /* OPTION_BUILD_LOADERS_JSM */ - /* JavaScript V8 */ - #if defined(OPTION_BUILD_LOADERS_JS) && !defined(OPTION_BUILD_LOADERS_NODE) /* TODO: NodeJS and V8 runtimes do not work at same time */ +/* JavaScript V8 */ +#if defined(OPTION_BUILD_LOADERS_JS) && !defined(OPTION_BUILD_LOADERS_NODE) /* TODO: NodeJS and V8 runtimes do not work at same time */ { - const char * js_scripts[] = - { + const char *js_scripts[] = { "divide.js", "third.js" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("js", js_scripts, sizeof(js_scripts) / sizeof(js_scripts[0]), NULL)); ret = metacall("say_divide", 32.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 8.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)8.0); metacall_value_destroy(ret); ret = metacall("some_text", "abc", "def"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "abcdef")); + EXPECT_STREQ(metacall_value_to_string(ret), "abcdef"); metacall_value_destroy(ret); ret = metacall("third_value", "abc", "def", "efg"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "efg")); + EXPECT_STREQ(metacall_value_to_string(ret), "efg"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_JS */ +#endif /* OPTION_BUILD_LOADERS_JS */ - /* Mock */ - #if defined(OPTION_BUILD_LOADERS_MOCK) +/* Mock */ +#if defined(OPTION_BUILD_LOADERS_MOCK) { - const char * mock_scripts[] = - { + const char *mock_scripts[] = { "empty.mock" }; - void * ret = NULL; + void *ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("mock", mock_scripts, sizeof(mock_scripts) / sizeof(mock_scripts[0]), NULL)); ret = metacall("my_empty_func"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) metacall_value_to_int(ret), (int) 1234); + EXPECT_EQ((int)metacall_value_to_int(ret), (int)1234); metacall_value_destroy(ret); ret = metacall("two_doubles", 3.14, 68.3); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 3.1416); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)3.1416); metacall_value_destroy(ret); ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((char) metacall_value_to_char(ret), (char) 'A'); + EXPECT_EQ((char)metacall_value_to_char(ret), (char)'A'); metacall_value_destroy(ret); ret = metacall("new_args", "goodbye"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "Hello World")); + EXPECT_STREQ(metacall_value_to_string(ret), "Hello World"); metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_MOCK */ +#endif /* OPTION_BUILD_LOADERS_MOCK */ - /* C# NetCore */ - #if defined(OPTION_BUILD_LOADERS_CS) +/* C# NetCore */ +#if defined(OPTION_BUILD_LOADERS_CS) { - const char * cs_scripts[] = - { + const char *cs_scripts[] = { "hello.cs" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("cs", cs_scripts, sizeof(cs_scripts) / sizeof(cs_scripts[0]), NULL)); - EXPECT_EQ((void *) NULL, (void *) metacall("Say", "Hello para with params!")); + EXPECT_EQ((void *)NULL, (void *)metacall("Say", "Hello para with params!")); } - #endif /* OPTION_BUILD_LOADERS_CS */ +#endif /* OPTION_BUILD_LOADERS_CS */ - /* C */ - #if defined(OPTION_BUILD_LOADERS_C) +/* C */ +#if defined(OPTION_BUILD_LOADERS_C) { - const char * c_scripts[] = - { + const char *c_scripts[] = { "compiled.c" }; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); + EXPECT_EQ((int)0, (int)metacall_load_from_file("c", c_scripts, sizeof(c_scripts) / sizeof(c_scripts[0]), NULL)); } - #endif /* OPTION_BUILD_LOADERS_C */ +#endif /* OPTION_BUILD_LOADERS_C */ - /* NodeJS */ - #if defined(OPTION_BUILD_LOADERS_NODE) +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) { - const char * node_scripts[] = - { + const char *node_scripts[] = { "nod.js" }; - const enum metacall_value_id hello_boy_double_ids[] = - { + const enum metacall_value_id hello_boy_double_ids[] = { METACALL_DOUBLE, METACALL_DOUBLE }; - void * ret = NULL; + const char buffer[] = + "function nodmem() {\n" + "\treturn 43;\n" + "\t}\n" + "module.exports = { nodmem };\n"; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); ret = metacallt("hello_boy", hello_boy_double_ids, 3.0, 4.0); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 7.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); metacall_value_destroy(ret); ret = metacall("lambda"); - EXPECT_NE((void *) NULL, (void *) ret); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_EQ((double) metacall_value_to_double(ret), (double) 15.0); + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); metacall_value_destroy(ret); /* TODO: Implement all remaining calls for nod.js */ + + ASSERT_EQ((int)0, (int)metacall_load_from_memory("node", buffer, sizeof(buffer), NULL)); + + ret = metacall("nodmem"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)43.0); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + +/* File */ +#if defined(OPTION_BUILD_LOADERS_FILE) + { + const char *file_scripts[] = { + "static.html", + "favicon.ico" + }; + + void *ret = NULL; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("file", file_scripts, sizeof(file_scripts) / sizeof(file_scripts[0]), NULL)); + + ret = metacall("static.html"); + + EXPECT_NE((void *)NULL, (void *)ret); + + std::cout << metacall_value_to_string(ret) << std::endl; + + metacall_value_destroy(ret); + + ret = metacall("favicon.ico"); + + EXPECT_NE((void *)NULL, (void *)ret); + + std::cout << metacall_value_to_string(ret) << std::endl; + + metacall_value_destroy(ret); } - #endif /* OPTION_BUILD_LOADERS_NODE */ +#endif /* OPTION_BUILD_LOADERS_FILE */ /* Print inspect information */ { @@ -445,13 +590,13 @@ TEST_F(metacall_test, DefaultConstructor) struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - void * allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - char * inspect_str = metacall_inspect(&size, allocator); + char *inspect_str = metacall_inspect(&size, allocator); - EXPECT_NE((char *) NULL, (char *) inspect_str); + EXPECT_NE((char *)NULL, (char *)inspect_str); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); std::cout << inspect_str << std::endl; @@ -460,5 +605,5 @@ TEST_F(metacall_test, DefaultConstructor) metacall_allocator_destroy(allocator); } - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } diff --git a/source/tests/metacall_test/source/metacall_test_split.cpp b/source/tests/metacall_test/source/metacall_test_split.cpp index 281d309152..0a19bce6a1 100644 --- a/source/tests/metacall_test/source/metacall_test_split.cpp +++ b/source/tests/metacall_test/source/metacall_test_split.cpp @@ -2,7 +2,7 @@ * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include #include @@ -34,17 +34,17 @@ class metacall_test : public testing::Test TEST_F(metacall_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); metacall_print_info(); - EXPECT_EQ((int) 0, (int) metacall_initialize()); + EXPECT_EQ((int)0, (int)metacall_initialize()); - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } class metacall_loader_test : public testing::Test @@ -52,232 +52,238 @@ class metacall_loader_test : public testing::Test public: metacall_loader_test() { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); - EXPECT_EQ((int) 0, (int) metacall_initialize()); + EXPECT_EQ((int)0, (int)metacall_initialize()); } ~metacall_loader_test() { - EXPECT_EQ((int) 0, (int) metacall_destroy()); + metacall_destroy(); } }; /* Python */ #if defined(OPTION_BUILD_LOADERS_PY) - TEST_F(metacall_loader_test, Python) - { - const long seven_multiples_limit = 10; +TEST_F(metacall_loader_test, Python) +{ + const long seven_multiples_limit = 10; - long iterator; + long iterator; - value ret = NULL; + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("example.py")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("example.py")); - ret = metacall("multiply", 5, 15); + ret = metacall("multiply", 5, 15); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) 75); + EXPECT_EQ((long)value_to_long(ret), (long)75); - value_destroy(ret); + value_destroy(ret); - log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); + log_write("metacall", LOG_LEVEL_DEBUG, "7's multiples dude!"); - for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) - { - ret = metacall("multiply", 7, iterator); + for (iterator = 0; iterator <= seven_multiples_limit; ++iterator) + { + ret = metacall("multiply", 7, iterator); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) (7 * iterator)); + EXPECT_EQ((long)value_to_long(ret), (long)(7 * iterator)); - value_destroy(ret); - } + value_destroy(ret); + } - ret = metacall("divide", 64.0, 2.0); + ret = metacall("divide", 64.0, 2.0); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 32.0); + EXPECT_EQ((double)value_to_double(ret), (double)32.0); - value_destroy(ret); + value_destroy(ret); - ret = metacall("sum", 1000, 3500); + ret = metacall("sum", 1000, 3500); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) 4500); + EXPECT_EQ((long)value_to_long(ret), (long)4500); - value_destroy(ret); + value_destroy(ret); - ret = metacall("sum", 3, 4); + ret = metacall("sum", 3, 4); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((long) value_to_long(ret), (long) 7); + EXPECT_EQ((long)value_to_long(ret), (long)7); - value_destroy(ret); + value_destroy(ret); - EXPECT_EQ((value) NULL, (value) metacall("hello")); + EXPECT_EQ((value)NULL, (value)metacall("hello")); - ret = metacall("strcat", "Hello ", "Universe"); + ret = metacall("strcat", "Hello ", "Universe"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello Universe")); + EXPECT_STREQ(value_to_string(ret), "Hello Universe"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_PY */ /* Ruby */ #if defined(OPTION_BUILD_LOADERS_RB) - TEST_F(metacall_loader_test, Ruby) - { - value ret = NULL; +TEST_F(metacall_loader_test, Ruby) +{ + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("hello.rb")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("hello.rb")); - ret = metacall("say_multiply", 5, 7); + ret = metacall("say_multiply", 5, 7); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) value_to_int(ret), (int) 35); + EXPECT_EQ((int)value_to_int(ret), (int)35); - value_destroy(ret); + value_destroy(ret); - EXPECT_EQ((void *) NULL, (void *) metacall("say_null")); + ret = metacall("say_null"); - ret = metacall("say_hello", "meta-programmer"); + EXPECT_NE((void *)NULL, (void *)ret); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello meta-programmer!")); + metacall_value_destroy(ret); - value_destroy(ret); - } + ret = metacall("say_hello", "meta-programmer"); + + EXPECT_NE((value)NULL, (value)ret); + + EXPECT_STREQ(value_to_string(ret), "Hello meta-programmer!"); + + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_RB */ /* JavaScript SpiderMonkey */ #if defined(OPTION_BUILD_LOADERS_JSM) - TEST_F(metacall_loader_test, JavascriptSpiderMonkey) - { - EXPECT_EQ((int) 0, (int) metacall_load_from_file("spider.jsm")); +TEST_F(metacall_loader_test, JavascriptSpiderMonkey) +{ + EXPECT_EQ((int)0, (int)metacall_load_from_file("spider.jsm")); - EXPECT_EQ((void *) NULL, (void *) metacall("say_spider", 8, 4)); - } + EXPECT_EQ((void *)NULL, (void *)metacall("say_spider", 8, 4)); +} #endif /* OPTION_BUILD_LOADERS_JSM */ /* JavaScript V8 */ #if defined(OPTION_BUILD_LOADERS_JS) - TEST_F(metacall_loader_test, JavascriptV8) - { - value ret = NULL; +TEST_F(metacall_loader_test, JavascriptV8) +{ + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("divide.js")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("divide.js")); - ret = metacall("say_divide", 32.0, 4.0); + ret = metacall("say_divide", 32.0, 4.0); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 8.0); + EXPECT_EQ((double)value_to_double(ret), (double)8.0); - value_destroy(ret); + value_destroy(ret); - ret = metacall("some_text", "abc", "def"); + ret = metacall("some_text", "abc", "def"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "abcdef")); + EXPECT_STREQ(value_to_string(ret), "abcdef"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_JS */ /* Mock */ #if defined(OPTION_BUILD_LOADERS_MOCK) - TEST_F(metacall_loader_test, Mock) - { - value ret = NULL; +TEST_F(metacall_loader_test, Mock) +{ + value ret = NULL; - EXPECT_EQ((int) 0, (int) metacall_load_from_file("empty.mock")); + EXPECT_EQ((int)0, (int)metacall_load_from_file("empty.mock")); - ret = metacall("my_empty_func"); + ret = metacall("my_empty_func"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) value_to_int(ret), (int) 1234); + EXPECT_EQ((int)value_to_int(ret), (int)1234); - value_destroy(ret); + value_destroy(ret); - ret = metacall("two_doubles", 3.14, 68.3); + ret = metacall("two_doubles", 3.14, 68.3); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((double) value_to_double(ret), (double) 3.1416); + EXPECT_EQ((double)value_to_double(ret), (double)3.1416); - value_destroy(ret); + value_destroy(ret); - ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); + ret = metacall("mixed_args", 'E', 16, 34L, 4.6, "hello"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((char) value_to_char(ret), (char) 'A'); + EXPECT_EQ((char)value_to_char(ret), (char)'A'); - value_destroy(ret); + value_destroy(ret); - ret = metacall("new_args", "goodbye"); + ret = metacall("new_args", "goodbye"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello World")); + EXPECT_STREQ(value_to_string(ret), "Hello World"); - value_destroy(ret); + value_destroy(ret); - ret = metacall("my_empty_func_str"); + ret = metacall("my_empty_func_str"); - EXPECT_NE((value) NULL, (value) ret); + EXPECT_NE((value)NULL, (value)ret); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(ret), "Hello World")); + EXPECT_STREQ(value_to_string(ret), "Hello World"); - value_destroy(ret); - } + value_destroy(ret); +} #endif /* OPTION_BUILD_LOADERS_MOCK */ /* C# Netcore */ #if defined(OPTION_BUILD_LOADERS_CS) - TEST_F(metacall_loader_test, CSharpNetCore) - { - EXPECT_EQ((int) 0, (int) metacall_load_from_file("hello.cs")); +TEST_F(metacall_loader_test, CSharpNetCore) +{ + EXPECT_EQ((int)0, (int)metacall_load_from_file("hello.cs")); - EXPECT_EQ((void *) NULL, (void *) metacall("Say", "Hello para with params!")); - } + EXPECT_EQ((void *)NULL, (void *)metacall("Say", "Hello para with params!")); +} #endif /* OPTION_BUILD_LOADERS_CS */ /* C */ #if defined(OPTION_BUILD_LOADERS_C) - TEST_F(metacall_loader_test, C) - { - EXPECT_EQ((int) 0, (int) metacall_load_from_file("compiled.c")); - } +TEST_F(metacall_loader_test, C) +{ + EXPECT_EQ((int)0, (int)metacall_load_from_file("compiled.c")); +} #endif /* OPTION_BUILD_LOADERS_C */ diff --git a/source/tests/metacall_typescript_call_map_test/CMakeLists.txt b/source/tests/metacall_typescript_call_map_test/CMakeLists.txt new file mode 100644 index 0000000000..4005debcf6 --- /dev/null +++ b/source/tests/metacall_typescript_call_map_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-call-map-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_call_map_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_call_map_test/source/main.cpp b/source/tests/metacall_typescript_call_map_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_call_map_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_call_map_test/source/metacall_typescript_call_map_test.cpp b/source/tests/metacall_typescript_call_map_test/source/metacall_typescript_call_map_test.cpp new file mode 100644 index 0000000000..82af7b2944 --- /dev/null +++ b/source/tests/metacall_typescript_call_map_test/source/metacall_typescript_call_map_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_call_map_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_call_map_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + static const char args_map[] = "{\"left\":10,\"right\":2}"; + + void *ret = metacallfms(metacall_function("typed_sum"), args_map, sizeof(args_map), allocator); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)12.0); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + } + + metacall_allocator_destroy(allocator); + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_jsx_default_test/CMakeLists.txt b/source/tests/metacall_typescript_jsx_default_test/CMakeLists.txt new file mode 100644 index 0000000000..32674e550f --- /dev/null +++ b/source/tests/metacall_typescript_jsx_default_test/CMakeLists.txt @@ -0,0 +1,158 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-jsx-default-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_jsx_default_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_jsx_default_test/source/main.cpp b/source/tests/metacall_typescript_jsx_default_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_jsx_default_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_jsx_default_test/source/metacall_typescript_jsx_default_test.cpp b/source/tests/metacall_typescript_jsx_default_test/source/metacall_typescript_jsx_default_test.cpp new file mode 100644 index 0000000000..609bb760e3 --- /dev/null +++ b/source/tests/metacall_typescript_jsx_default_test/source/metacall_typescript_jsx_default_test.cpp @@ -0,0 +1,51 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_jsx_default_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_jsx_default_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *jsx_scripts[] = { + "server.jsx" + }; + + /* Load scripts */ + EXPECT_EQ((int)1, (int)metacall_load_from_file("ts", jsx_scripts, sizeof(jsx_scripts) / sizeof(jsx_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_node_test/CMakeLists.txt b/source/tests/metacall_typescript_node_test/CMakeLists.txt new file mode 100644 index 0000000000..9dbd14de9d --- /dev/null +++ b/source/tests/metacall_typescript_node_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-node-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_node_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_node_test/source/main.cpp b/source/tests/metacall_typescript_node_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_node_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_node_test/source/metacall_typescript_node_test.cpp b/source/tests/metacall_typescript_node_test/source/metacall_typescript_node_test.cpp new file mode 100644 index 0000000000..4285ca809f --- /dev/null +++ b/source/tests/metacall_typescript_node_test/source/metacall_typescript_node_test.cpp @@ -0,0 +1,142 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_node_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_node_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + void *ret = NULL; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + ret = metacall("typed_sum", 3.0, 4.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); + + metacall_value_destroy(ret); + + /* Test arrays */ + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_double(3.0); + array_value[1] = metacall_value_create_double(5.0); + array_value[2] = metacall_value_create_double(7.0); + + ret = metacallv("typed_array", array_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(array_args[0]); + + /* Test records */ + void *record_args[] = { + metacall_value_create_map(NULL, 1) + }; + + void **map_value = metacall_value_to_map(record_args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla = metacall_value_to_array(map_value[0]); + + static const char key[] = "element"; + + tupla[0] = metacall_value_create_string(key, sizeof(key) - 1); + tupla[1] = metacall_value_create_double(6.0); + + ret = metacallv("object_record", record_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(record_args[0]); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + +/* NodeJS */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + const char *node_scripts[] = { + "nod.js" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("node", node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_require_test/CMakeLists.txt b/source/tests/metacall_typescript_require_test/CMakeLists.txt new file mode 100644 index 0000000000..d4d51e4660 --- /dev/null +++ b/source/tests/metacall_typescript_require_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-require-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_require_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_require_test/source/main.cpp b/source/tests/metacall_typescript_require_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_require_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_require_test/source/metacall_typescript_require_test.cpp b/source/tests/metacall_typescript_require_test/source/metacall_typescript_require_test.cpp new file mode 100644 index 0000000000..b94e43fa36 --- /dev/null +++ b/source/tests/metacall_typescript_require_test/source/metacall_typescript_require_test.cpp @@ -0,0 +1,81 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_require_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_require_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "badrequire/index.ts" + }; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + void *ret = metacall("isExported"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((boolean)metacall_value_to_bool(ret), (boolean)1L); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_test/CMakeLists.txt b/source/tests/metacall_typescript_test/CMakeLists.txt new file mode 100644 index 0000000000..9af3378ebb --- /dev/null +++ b/source/tests/metacall_typescript_test/CMakeLists.txt @@ -0,0 +1,159 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/typedfunc +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_test/source/main.cpp b/source/tests/metacall_typescript_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp b/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp new file mode 100644 index 0000000000..9ebd5f9f30 --- /dev/null +++ b/source/tests/metacall_typescript_test/source/metacall_typescript_test.cpp @@ -0,0 +1,130 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_typescript_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_typescript_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *ts_scripts[] = { + "typedfunc/typedfunc.ts" + }; + + void *ret = NULL; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", ts_scripts, sizeof(ts_scripts) / sizeof(ts_scripts[0]), NULL)); + + /* Test typed sum */ + ret = metacall("typed_sum", 3.0, 4.0); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)7.0); + + metacall_value_destroy(ret); + + /* Test arrays */ + void *array_args[] = { + metacall_value_create_array(NULL, 3) + }; + + void **array_value = metacall_value_to_array(array_args[0]); + + array_value[0] = metacall_value_create_double(3.0); + array_value[1] = metacall_value_create_double(5.0); + array_value[2] = metacall_value_create_double(7.0); + + ret = metacallv("typed_array", array_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)15.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(array_args[0]); + + /* Test records */ + void *record_args[] = { + metacall_value_create_map(NULL, 1) + }; + + void **map_value = metacall_value_to_map(record_args[0]); + + map_value[0] = metacall_value_create_array(NULL, 2); + + void **tupla = metacall_value_to_array(map_value[0]); + + static const char key[] = "element"; + + tupla[0] = metacall_value_create_string(key, sizeof(key) - 1); + tupla[1] = metacall_value_create_double(6.0); + + ret = metacallv("object_record", record_args); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_EQ((double)metacall_value_to_double(ret), (double)6.0); + + metacall_value_destroy(ret); + + metacall_value_destroy(record_args[0]); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_tsx_loop_fail_test/CMakeLists.txt b/source/tests/metacall_typescript_tsx_loop_fail_test/CMakeLists.txt new file mode 100644 index 0000000000..ca6465387a --- /dev/null +++ b/source/tests/metacall_typescript_tsx_loop_fail_test/CMakeLists.txt @@ -0,0 +1,182 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-tsx-loop-fail-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_tsx_loop_fail_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_THREAD_SANITIZER AND OPTION_BUILD_LOADERS_CS) + # TODO: This test fails when run with thread sanitizer (this happens when C# loader is enabled): + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=13916) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 (ld-linux-x86-64.so.2+0x28df) + # #2 (libruby-2.7.so.2.7+0x237879) + # #3 simple_netcore_create /usr/local/metacall/source/loaders/cs_loader/source/simple_netcore.cpp:42 (libcs_loaderd.so+0x108de) + # #4 cs_loader_impl_initialize /usr/local/metacall/source/loaders/cs_loader/source/cs_loader_impl.c:236 (libcs_loaderd.so+0xf5fe) + # #5 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:367 (libmetacalld.so+0x30673) + # #6 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:822 (libmetacalld.so+0x30888) + # #7 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #8 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #9 node_loader_port_load_from_file_export(napi_env__*, napi_callback_info__*) /usr/local/metacall/source/loaders/node_loader/source/node_loader_port.cpp:395 (libnode_loaderd.so+0x113c5) + # #10 (libnode.so.72+0x7b6344) + # #11 node_loader_impl_async_func_call_safe /usr/local/metacall/source/loaders/node_loader/source/node_loader_impl.cpp:2040 (libnode_loaderd.so+0xe2e8) + # #12 (libnode.so.72+0x7b6344) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/lib64/ld-linux-x86-64.so.2+0x28df) + # + # For solving this, we should enable C# support for sanitizers and debug it properly + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_tsx_loop_fail_test/source/main.cpp b/source/tests/metacall_typescript_tsx_loop_fail_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_tsx_loop_fail_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_tsx_loop_fail_test/source/metacall_typescript_tsx_loop_fail_test.cpp b/source/tests/metacall_typescript_tsx_loop_fail_test/source/metacall_typescript_tsx_loop_fail_test.cpp new file mode 100644 index 0000000000..54a278e394 --- /dev/null +++ b/source/tests/metacall_typescript_tsx_loop_fail_test/source/metacall_typescript_tsx_loop_fail_test.cpp @@ -0,0 +1,72 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_tsx_loop_fail_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_tsx_loop_fail_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *tsx_scripts[] = { + "loopfail/loopfail.tsx" + }; + + /* Load scripts */ + EXPECT_EQ((int)1, (int)metacall_load_from_file("ts", tsx_scripts, sizeof(tsx_scripts) / sizeof(tsx_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_typescript_tsx_test/CMakeLists.txt b/source/tests/metacall_typescript_tsx_test/CMakeLists.txt new file mode 100644 index 0000000000..53682fa434 --- /dev/null +++ b/source/tests/metacall_typescript_tsx_test/CMakeLists.txt @@ -0,0 +1,160 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_TS + OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_NODE OR NOT OPTION_BUILD_SCRIPTS_TS) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-typescript-tsx-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_typescript_tsx_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ + # The test must run where the tsconfig is + WORKING_DIRECTORY ${LOADER_SCRIPT_PATH}/templating +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + ts_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_typescript_tsx_test/source/main.cpp b/source/tests/metacall_typescript_tsx_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_typescript_tsx_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp b/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp new file mode 100644 index 0000000000..52a406737c --- /dev/null +++ b/source/tests/metacall_typescript_tsx_test/source/metacall_typescript_tsx_test.cpp @@ -0,0 +1,83 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_tsx_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_tsx_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* TypeScript */ +#if defined(OPTION_BUILD_LOADERS_TS) + { + const char *tsx_scripts[] = { + "templating.tsx" + }; + + void *ret = NULL; + + /* Load scripts */ + EXPECT_EQ((int)0, (int)metacall_load_from_file("ts", tsx_scripts, sizeof(tsx_scripts) / sizeof(tsx_scripts[0]), NULL)); + + /* Test templating function */ + ret = metacall("hello", "metaprogrammer"); + + EXPECT_NE((void *)NULL, (void *)ret); + + EXPECT_STREQ(metacall_value_to_string(ret), "

Hello metaprogrammer

"); + + metacall_value_destroy(ret); + } +#endif /* OPTION_BUILD_LOADERS_TS */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + metacall_destroy(); +} diff --git a/source/tests/metacall_version_test/CMakeLists.txt b/source/tests/metacall_version_test/CMakeLists.txt new file mode 100644 index 0000000000..4d93e4fec3 --- /dev/null +++ b/source/tests/metacall_version_test/CMakeLists.txt @@ -0,0 +1,143 @@ +# +# Executable name and options +# + +# Target name +set(target metacall-version-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_version_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_version_test/source/main.cpp b/source/tests/metacall_version_test/source/main.cpp new file mode 100644 index 0000000000..5820341294 --- /dev/null +++ b/source/tests/metacall_version_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_version_test/source/metacall_version_test.cpp b/source/tests/metacall_version_test/source/metacall_version_test.cpp new file mode 100644 index 0000000000..ef703c79a3 --- /dev/null +++ b/source/tests/metacall_version_test/source/metacall_version_test.cpp @@ -0,0 +1,37 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +class metacall_version_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_version_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_STREQ(METACALL_VERSION, metacall_version_str()); + + /* TODO: Test other version functions */ +} diff --git a/source/tests/metacall_wasm_python_port_test/CMakeLists.txt b/source/tests/metacall_wasm_python_port_test/CMakeLists.txt new file mode 100644 index 0000000000..7233aa5796 --- /dev/null +++ b/source/tests/metacall_wasm_python_port_test/CMakeLists.txt @@ -0,0 +1,219 @@ +# Check if this loader is enabled +if(NOT (OPTION_BUILD_LOADERS AND OPTION_BUILD_LOADERS_WASM AND OPTION_BUILD_LOADERS_PY + AND OPTION_BUILD_SCRIPTS AND OPTION_BUILD_SCRIPTS_WASM AND OPTION_BUILD_SCRIPTS_PY + AND OPTION_BUILD_PORTS AND OPTION_BUILD_PORTS_PY)) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-wasm-python-port-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_wasm_python_port_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: Address sanitizer seems to break with WASM loading, we should either review this or instrument properly wasmtime library + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + wasm_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizers: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14007) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 alloc::alloc::alloc::h8eea693b11397046 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 (libwasmtime.so+0x823ef8) + # #2 alloc::alloc::Global::alloc_impl::hdd19e4608fbda4e9 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:166 (libwasmtime.so+0x823ef8) + # #3 _$LT$alloc..alloc..Global$u20$as$u20$core..alloc..Allocator$GT$::allocate::hf61e18fb7f49870e /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:226 (libwasmtime.so+0x823ef8) + # #4 alloc::alloc::exchange_malloc::h1859a431711d6c17 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:316 (libwasmtime.so+0x823ef8) + # #5 alloc::boxed::Box$LT$T$GT$::new::h4ef787c2f87c345b /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:185 (libwasmtime.so+0x823ef8) + # #6 _$LT$alloc..boxed..Box$LT$T$GT$$u20$as$u20$core..convert..From$LT$T$GT$$GT$::from::h26993fc6ee782f3a /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:1200 (libwasmtime.so+0x823ef8) + # #7 std::sys_common::mutex::MovableMutex::new::hf02abe706afa4cd7 library/std/src/sys_common/mutex.rs:64 (libwasmtime.so+0x823ef8) + # #8 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #9 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #10 py_loader_impl_function_type_invoke /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:2003 (libpy_loaderd.so+0xe01f) + # #11 (libpython3.9.so.1.0+0x10cc73) + # #12 py_loader_impl_load_from_file_relative /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3102 (libpy_loaderd.so+0xb5ae) + # #13 py_loader_impl_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3193 (libpy_loaderd.so+0xb7db) + # #14 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:838 (libmetacalld.so+0x30944) + # #15 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #16 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #17 metacall_wasm_python_port_test_CallWasmFromPython_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp:45 (metacall-wasm-python-port-testd+0x209c4) + # #18 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-python-port-testd+0x4df46) + # #19 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 in alloc::alloc::alloc::h8eea693b11397046 + # + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14007) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 (libwasmtime.so+0x57da03) + # #2 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #3 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #4 py_loader_impl_function_type_invoke /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:2003 (libpy_loaderd.so+0xe01f) + # #5 (libpython3.9.so.1.0+0x10cc73) + # #6 py_loader_impl_load_from_file_relative /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3102 (libpy_loaderd.so+0xb5ae) + # #7 py_loader_impl_load_from_file /usr/local/metacall/source/loaders/py_loader/source/py_loader_impl.c:3193 (libpy_loaderd.so+0xb7db) + # #8 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:838 (libmetacalld.so+0x30944) + # #9 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:307 (libmetacalld.so+0x2e0d1) + # #10 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:348 (libmetacalld.so+0x32bbf) + # #11 metacall_wasm_python_port_test_CallWasmFromPython_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp:45 (metacall-wasm-python-port-testd+0x209c4) + # #12 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-python-port-testd+0x4df46) + # #13 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/usr/local/metacall/build/wasmtime/wasmtime-v0.28.0-x86_64-linux-c-api/lib/libwasmtime.so+0x57da03) in _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 + # + # ... + # + # + # For solving this, we should enable WASM support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_wasm_python_port_test/source/main.cpp b/source/tests/metacall_wasm_python_port_test/source/main.cpp new file mode 100644 index 0000000000..ae1380d351 --- /dev/null +++ b/source/tests/metacall_wasm_python_port_test/source/main.cpp @@ -0,0 +1,27 @@ +/* + * WebAssembly Loader Tests by Parra Studios + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp b/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp new file mode 100644 index 0000000000..f233b6a52c --- /dev/null +++ b/source/tests/metacall_wasm_python_port_test/source/metacall_wasm_python_port_test.cpp @@ -0,0 +1,46 @@ +/* + * WebAssembly Loader Tests by Parra Studios + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_wasm_python_port_test : public testing::Test +{ +protected: + void SetUp() override + { + metacall_initialize(); + } + + void TearDown() override + { + metacall_destroy(); + } +}; + +TEST_F(metacall_wasm_python_port_test, CallWasmFromPython) +{ + const char *scripts[] = { + "wasm.py" + }; + + ASSERT_EQ(0, metacall_load_from_file("py", scripts, 1, NULL)); +} diff --git a/source/tests/metacall_wasm_test/CMakeLists.txt b/source/tests/metacall_wasm_test/CMakeLists.txt new file mode 100644 index 0000000000..6ceca66927 --- /dev/null +++ b/source/tests/metacall_wasm_test/CMakeLists.txt @@ -0,0 +1,205 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_WASM OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_WASM) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-wasm-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_wasm_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define dependencies +# + +add_dependencies(${target} + wasm_loader +) + +# +# Define test +# + +if(OPTION_BUILD_ADDRESS_SANITIZER) + # TODO: Address sanitizer seems to break with WASM loading, + # we should either review this or instrument properly wasmtime library + return() +endif() + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_THREAD_SANITIZER) + # TODO: This test fails when run with thread sanitizer: + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14006) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 alloc::alloc::alloc::h8eea693b11397046 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 (libwasmtime.so+0x823ef8) + # #2 alloc::alloc::Global::alloc_impl::hdd19e4608fbda4e9 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:166 (libwasmtime.so+0x823ef8) + # #3 _$LT$alloc..alloc..Global$u20$as$u20$core..alloc..Allocator$GT$::allocate::hf61e18fb7f49870e /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:226 (libwasmtime.so+0x823ef8) + # #4 alloc::alloc::exchange_malloc::h1859a431711d6c17 /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:316 (libwasmtime.so+0x823ef8) + # #5 alloc::boxed::Box$LT$T$GT$::new::h4ef787c2f87c345b /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:185 (libwasmtime.so+0x823ef8) + # #6 _$LT$alloc..boxed..Box$LT$T$GT$$u20$as$u20$core..convert..From$LT$T$GT$$GT$::from::h26993fc6ee782f3a /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/boxed.rs:1200 (libwasmtime.so+0x823ef8) + # #7 std::sys_common::mutex::MovableMutex::new::hf02abe706afa4cd7 library/std/src/sys_common/mutex.rs:64 (libwasmtime.so+0x823ef8) + # #8 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #9 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #10 metacall /usr/local/metacall/source/metacall/source/metacall.c:513 (libmetacalld.so+0x33284) + # #11 metacall_wasm_test_CallFunctions_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp:180 (metacall-wasm-testd+0x271ce) + # #12 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-testd+0x5b606) + # #13 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal /rustc/9bc8c42bb2f19e745a63f3445f1ac248fb015e53/library/alloc/src/alloc.rs:86 in alloc::alloc::alloc::h8eea693b11397046 + # + # + # WARNING: ThreadSanitizer: signal-unsafe call inside of a signal (pid=14006) + # #0 malloc ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:647 (libtsan.so.2+0x3ebb8) + # #1 _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 (libwasmtime.so+0x57da03) + # #2 function_wasm_interface_invoke /usr/local/metacall/source/loaders/wasm_loader/source/wasm_loader_function.c:172 (libwasm_loaderd.so+0x47db) + # #3 function_call /usr/local/metacall/source/reflect/source/reflect_function.c:607 (libmetacalld.so+0x241e3) + # #4 metacall /usr/local/metacall/source/metacall/source/metacall.c:513 (libmetacalld.so+0x33284) + # #5 metacall_wasm_test_CallFunctions_Test::TestBody() /usr/local/metacall/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp:180 (metacall-wasm-testd+0x271ce) + # #6 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) (metacall-wasm-testd+0x5b606) + # #7 (libc.so.6+0x29209) + # + # SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal (/usr/local/metacall/build/wasmtime/wasmtime-v0.28.0-x86_64-linux-c-api/lib/libwasmtime.so+0x57da03) in _ZN3std4sync4once4Once9call_once28_$u7b$$u7b$closure$u7d$$u7d$17h00ca29eb50828eb3E.llvm.2373258818034603441 + # + # ... + # + # + # For solving this, we should enable WASM support for sanitizers and debug it properly + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall_wasm_test/source/main.cpp b/source/tests/metacall_wasm_test/source/main.cpp new file mode 100644 index 0000000000..ae1380d351 --- /dev/null +++ b/source/tests/metacall_wasm_test/source/main.cpp @@ -0,0 +1,27 @@ +/* + * WebAssembly Loader Tests by Parra Studios + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp b/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp new file mode 100644 index 0000000000..0042e45b7c --- /dev/null +++ b/source/tests/metacall_wasm_test/source/metacall_wasm_test.cpp @@ -0,0 +1,196 @@ +/* + * WebAssembly Loader Tests by Parra Studios + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_wasm_test : public testing::Test +{ +public: +}; + +void TestFunction(void *handle, const char *name, const std::vector &expected_param_types, enum metacall_value_id expected_return_type) +{ + void *func = metacall_handle_function(handle, name); + + ASSERT_NE((void *)NULL, (void *)func); + ASSERT_EQ((size_t)expected_param_types.size(), (size_t)metacall_function_size(func)); + + for (size_t i = 0; i < expected_param_types.size(); ++i) + { + enum metacall_value_id param_type; + ASSERT_EQ((int)0, (int)metacall_function_parameter_type(func, i, ¶m_type)); + ASSERT_EQ((enum metacall_value_id)expected_param_types[i], (enum metacall_value_id)param_type); + } + + enum metacall_value_id return_type; + ASSERT_EQ((int)0, (int)metacall_function_return_type(func, &return_type)); + ASSERT_EQ((enum metacall_value_id)expected_return_type, (enum metacall_value_id)return_type); +} + +TEST_F(metacall_wasm_test, Default) +{ + ASSERT_EQ((int)0, (int)metacall_initialize()); + + /* LoadBinaryFromMemory */ + { + /* See https://webassembly.github.io/spec/core/binary/modules.html#binary-module */ + const char empty_module[] = { + 0x00, 0x61, 0x73, 0x6d, // Magic bytes + 0x01, 0x00, 0x00, 0x00 // Version + }; + ASSERT_EQ((int)0, (int)metacall_load_from_memory("wasm", empty_module, sizeof(empty_module), NULL)); + + const char invalid_module[] = { 0 }; + ASSERT_NE((int)0, (int)metacall_load_from_memory("wasm", invalid_module, sizeof(invalid_module), NULL)); + } + + /* LoadTextFromMemory */ + { + const char *empty_module = "(module)"; + ASSERT_EQ((int)0, (int)metacall_load_from_memory("wasm", empty_module, strlen(empty_module), NULL)); + + const char *invalid_module = "(invalid)"; + ASSERT_NE((int)0, (int)metacall_load_from_memory("wasm", invalid_module, strlen(invalid_module), NULL)); + } + + /* LoadFromFile */ + { + const char *empty_module_filename = "empty_module.wat"; + const char *invalid_module_filename = "invalid_module.wat"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("wasm", &empty_module_filename, 1, NULL)); + ASSERT_NE((int)0, (int)metacall_load_from_file("wasm", &invalid_module_filename, 1, NULL)); + } + + /* LoadFromPackage */ + { + ASSERT_EQ((int)0, (int)metacall_load_from_package("wasm", "empty_module.wasm", NULL)); + ASSERT_NE((int)0, (int)metacall_load_from_package("wasm", "invalid_module.wasm", NULL)); + } + + /* DiscoverFunctions & CallFunctions */ + { + const char *functions_module_filename = "functions.wat"; + void *handle = NULL; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("wasm", &functions_module_filename, 1, &handle)); + + ASSERT_EQ((void *)NULL, (void *)metacall_handle_function(handle, "does_not_exist")); + + TestFunction(handle, "none_ret_none", {}, METACALL_INVALID); + TestFunction(handle, "i64_ret_none", { METACALL_LONG }, METACALL_INVALID); + TestFunction(handle, "i32_f32_i64_f64_ret_none", { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }, METACALL_INVALID); + TestFunction(handle, "none_ret_i32", {}, METACALL_INT); + TestFunction(handle, "none_ret_i32_f32_i64_f64", {}, METACALL_ARRAY); + TestFunction(handle, "i32_f32_i64_f64_ret_i32_f32_i64_f64", { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }, METACALL_ARRAY); + + void *ret = metacallht_s(handle, "none_ret_none", {}, 0); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + const enum metacall_value_id i64_ret_none_ids[] = { METACALL_LONG }; + ret = metacallht_s(handle, "i64_ret_none", i64_ret_none_ids, 1, 0L); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + const enum metacall_value_id i32_f32_i64_f64_ret_none_ids[] = { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }; + ret = metacallht_s(handle, "i32_f32_i64_f64_ret_none", i32_f32_i64_f64_ret_none_ids, 4, 0, 0.0f, 0L, 0.0); + ASSERT_NE((void *)NULL, (void *)ret); + ASSERT_EQ((enum metacall_value_id)METACALL_NULL, (enum metacall_value_id)metacall_value_id(ret)); + metacall_value_destroy(ret); + + ret = metacallht_s(handle, "none_ret_i32", {}, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)1, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + + ret = metacallht_s(handle, "none_ret_i32_f32_i64_f64", {}, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_ARRAY, (enum metacall_value_id)metacall_value_id(ret)); + + void **values = metacall_value_to_array(ret); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(values[0])); + ASSERT_EQ((enum metacall_value_id)METACALL_FLOAT, (enum metacall_value_id)metacall_value_id(values[1])); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(values[2])); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(values[3])); + ASSERT_EQ((int)1, (int)metacall_value_to_int(values[0])); + ASSERT_EQ((float)2.0f, (float)metacall_value_to_float(values[1])); + ASSERT_EQ((long)3, (long)metacall_value_to_long(values[2])); + ASSERT_EQ((double)4.0, (double)metacall_value_to_double(values[3])); + metacall_value_destroy(ret); + + const enum metacall_value_id i32_f32_i64_f64_ret_i32_f32_i64_f64_ids[] = { METACALL_INT, METACALL_FLOAT, METACALL_LONG, METACALL_DOUBLE }; + ret = metacallht_s(handle, "i32_f32_i64_f64_ret_i32_f32_i64_f64", i32_f32_i64_f64_ret_i32_f32_i64_f64_ids, 4, 0, 0, 0, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_ARRAY, (enum metacall_value_id)metacall_value_id(ret)); + + values = metacall_value_to_array(ret); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(values[0])); + ASSERT_EQ((enum metacall_value_id)METACALL_FLOAT, (enum metacall_value_id)metacall_value_id(values[1])); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(values[2])); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(values[3])); + ASSERT_EQ((int)1, (int)metacall_value_to_int(values[0])); + ASSERT_EQ((float)2.0f, (float)metacall_value_to_float(values[1])); + ASSERT_EQ((long)3, (long)metacall_value_to_long(values[2])); + ASSERT_EQ((double)4.0, (double)metacall_value_to_double(values[3])); + metacall_value_destroy(ret); + +// It should exit with illegal instruction +#if defined(unix) || defined(__unix__) || defined(__unix) || \ + defined(linux) || defined(__linux__) || defined(__linux) || defined(__gnu_linux) + ASSERT_EXIT((metacallht_s(handle, "trap", {}, 0), exit(0)), ::testing::ExitedWithCode(0), ".*"); +#endif + } + + /* LinkModules */ + { + const char *modules[] = { + "exports1.wat", + "exports2.wat", + "imports.wat" + }; + + ASSERT_EQ((int)0, (int)metacall_load_from_file("wasm", modules, sizeof(modules) / sizeof(modules[0]), NULL)); + + void *ret = metacall("duplicate_func_i32"); + ASSERT_EQ((enum metacall_value_id)METACALL_INT, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((int)1, (int)metacall_value_to_int(ret)); + metacall_value_destroy(ret); + + ret = metacall("duplicate_func_i64"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(ret)); + ASSERT_EQ((long)2, (long)metacall_value_to_long(ret)); + metacall_value_destroy(ret); + } + + /* InvalidLinkModules */ + { + const char *modules[] = { + "exports1.wat", + "imports.wat" + }; + + ASSERT_EQ((int)1, (int)metacall_load_from_file("wasm", modules, sizeof(modules) / sizeof(modules[0]), NULL)); + } + + metacall_destroy(); +} diff --git a/source/tests/node_loader_test/CMakeLists.txt b/source/tests/node_loader_test/CMakeLists.txt deleted file mode 100644 index d1077ffe55..0000000000 --- a/source/tests/node_loader_test/CMakeLists.txt +++ /dev/null @@ -1,146 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target node-loader-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/node_loader_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::configuration -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/node_loader_test/source/main.cpp b/source/tests/node_loader_test/source/main.cpp deleted file mode 100644 index cdb6abc837..0000000000 --- a/source/tests/node_loader_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/node_loader_test/source/node_loader_test.cpp b/source/tests/node_loader_test/source/node_loader_test.cpp deleted file mode 100644 index 173bb8331c..0000000000 --- a/source/tests/node_loader_test/source/node_loader_test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class node_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(node_loader_test, DefaultConstructor) -{ - const loader_naming_tag tag = "node"; - - const loader_naming_path names[] = - { - "nod.js" - }; - - const loader_naming_path handle_names[] = - { - "nod" - }; - - const size_t size = sizeof(names) / sizeof(names[0]); - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) loader_load_from_file(tag, names, size, NULL)); - - /* TODO: Test load from memory */ - - for (size_t index = 0; index < size; ++index) - { - void * handle = loader_get_handle(tag, handle_names[index]); - - EXPECT_NE((void *) NULL, (void *) handle); - - EXPECT_EQ((int) 0, (int) loader_clear(handle)); - } - - EXPECT_EQ((int) 0, (int) loader_unload()); -} diff --git a/source/tests/portability_path_test/CMakeLists.txt b/source/tests/portability_path_test/CMakeLists.txt new file mode 100644 index 0000000000..b95e327189 --- /dev/null +++ b/source/tests/portability_path_test/CMakeLists.txt @@ -0,0 +1,136 @@ +# +# Executable name and options +# + +# Target name +set(target portability-path-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/portability_path_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::portability +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/portability_path_test/source/main.cpp b/source/tests/portability_path_test/source/main.cpp new file mode 100644 index 0000000000..24fae6af69 --- /dev/null +++ b/source/tests/portability_path_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/portability_path_test/source/portability_path_test.cpp b/source/tests/portability_path_test/source/portability_path_test.cpp new file mode 100644 index 0000000000..f4c638c2f0 --- /dev/null +++ b/source/tests/portability_path_test/source/portability_path_test.cpp @@ -0,0 +1,770 @@ +/* +* Loader Library by Parra Studios +* Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +* +* A library for loading executable code at run-time into a process. +* +*/ + +#include + +#include + +#include + +#define NAME_SIZE ((size_t)PORTABILITY_PATH_SIZE / 2) +#define PATH_SIZE ((size_t)PORTABILITY_PATH_SIZE) + +typedef char string_name[NAME_SIZE]; +typedef char string_path[PATH_SIZE]; + +class portability_path_test : public testing::Test +{ +public: +}; + +TEST_F(portability_path_test, portability_path_test_path_get_module_name) +{ + static const char base[] = "/a/b/c/asd.txt"; + static const char result[] = "asd"; + static const char extension[] = "txt"; + + string_name name; + + size_t size = portability_path_get_module_name(base, sizeof(base), extension, sizeof(extension), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_module_name_without_extension) +{ + static const char base[] = "/a/b/c/asd"; + static const char result[] = "asd"; + static const char extension[] = "txt"; + + string_name name; + + size_t size = portability_path_get_module_name(base, sizeof(base), extension, sizeof(extension), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_module_name_with_random_extension) +{ + static const char base[] = "/a/b/c/asd.etc.asd"; + static const char result[] = "asd.etc.asd"; + static const char extension[] = "txt"; + + string_name name; + + size_t size = portability_path_get_module_name(base, sizeof(base), extension, sizeof(extension), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_null) +{ + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(NULL, 0, name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_empty) +{ + static const char base[] = ""; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name) +{ + static const char base[] = "/a/b/c/asd.txt"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_end_dot) +{ + static const char base[] = "/a/b/c/asd."; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_without_dot) +{ + static const char base[] = "/a/b/c/asd"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_dot_in_path) +{ + static const char base[] = "/a/b.c/d/asd"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_dot_in_path_and_name) +{ + static const char base[] = "/a/b.c/d/asd.txt"; + static const char result[] = "asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_only_separator_dot) +{ + static const char base[] = "/."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_only_dot) +{ + static const char base[] = "/a/b/c/."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_two_dots) +{ + static const char base[] = "/a/b/c/.."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_three_dots) +{ + static const char base[] = "/a/b/c/..."; + static const char result[] = ""; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_only_extension) +{ + static const char base[] = "/a/b/c/.asd"; + static const char result[] = ".asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_double_extension) +{ + static const char base[] = "/a/b/c/.asd.yes"; + static const char result[] = ".asd"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_triple_extension) +{ + static const char base[] = "/a/b/c/.asd.yes.no"; + static const char result[] = ".asd.yes"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_path_get_name_nullchar) +{ + static const char base[] = "/home/yeet/.nvm/versions/node/v18.20.3/bin/node"; + static const char result[] = "node"; + + string_name name; + + size_t size = portability_path_get_name(base, sizeof(base), name, NAME_SIZE); + + EXPECT_STREQ(name, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_path_of_path) +{ + static const char base[] = "/a/b/c/"; + static const char result[] = "/a/b/c/"; + + string_path path; + + size_t size = portability_path_get_directory(base, sizeof(base), path, PATH_SIZE); + + EXPECT_STREQ(path, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_path_of_filepath) +{ + static const char base[] = "/a/b/c/asd"; + static const char result[] = "/a/b/c/"; + + string_path path; + + size_t size = portability_path_get_directory(base, sizeof(base), path, PATH_SIZE); + + EXPECT_STREQ(path, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_relative) +{ + static const char base[] = "/a/b/c/"; + static const char path[] = "/a/b/c/abc"; + static const char result[] = "abc"; + + string_path relative; + + size_t size = portability_path_get_relative(base, sizeof(base), path, sizeof(path), relative, PATH_SIZE); + + EXPECT_STREQ(relative, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_get_relative_fail) +{ + static const char base[] = "/this/is/not/shared/with/path"; + static const char path[] = "/a/b/c/abc"; + static const char result[] = "a/b/c/abc"; + + string_path relative; + + size_t size = portability_path_get_relative(base, sizeof(base), path, sizeof(path), relative, PATH_SIZE); + + EXPECT_STREQ(relative, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_none_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_left_slash) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = "e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_right_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "/e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_both_slash) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = "/e/f/g/"; + static const char result[] = "/a/b/c/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_left_empty) +{ + static const char left[] = ""; + static const char right[] = "/e/f/g/"; + static const char result[] = "/e/f/g/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_right_empty) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = ""; + static const char result[] = "/a/b/c/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_right_empty_non_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = ""; + static const char result[] = "/a/b/c/"; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_join_both_empty) +{ + static const char left[] = ""; + static const char right[] = ""; + static const char result[] = ""; + + string_path join; + + size_t size = portability_path_join(left, sizeof(left), right, sizeof(right), join, PATH_SIZE); + + EXPECT_STREQ(join, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_dot) +{ + static const char path[] = "./a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_double_dot) +{ + static const char path[] = "../a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_many_dot) +{ + static const char path[] = "./././././a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_many_double_dot) +{ + static const char path[] = "../../../../../a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_dot_non_slash) +{ + static const char path[] = ".a/b/c"; + static const char result[] = ".a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_many_dot_non_slash) +{ + static const char path[] = "..a/b/c"; + static const char result[] = "..a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_begin_invalid) +{ + static const char path[] = "..././.../...../a/b/c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_double_dot) +{ + static const char path[] = "../a/b/../c"; + static const char result[] = "a/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_double_dot_all) +{ + static const char path[] = "../a/b/../../c"; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_double_dot_break) +{ + static const char path[] = "../a/b/../../../c"; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_dot) +{ + static const char path[] = "../a/./././b/./././c"; + static const char result[] = "a/b/c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_middle_mixed_dot) +{ + static const char path[] = "../a/./././../b/././.././../c"; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_end_dot) +{ + static const char path[] = "../a/./././../b/././.././../c/."; + static const char result[] = "c"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_end_double_dot) +{ + static const char path[] = "../a/./././b/././../c/.."; + static const char result[] = "a"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_end_mixed_dot) +{ + static const char path[] = "../a/b/c/.././../."; + static const char result[] = "a"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_absolute_end_mixed_dot) +{ + static const char path[] = "/a/b/c/../../../."; + static const char result[] = "/"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_absolute_end_dot) +{ + static const char path[] = "/."; + static const char result[] = "/"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_relative_begin_end_dot) +{ + static const char path[] = "./."; + static const char result[] = "."; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_canonical_absolute_end_many_dot) +{ + static const char path[] = "/./././"; + static const char result[] = "/"; + + string_path canonical; + + size_t size = portability_path_canonical(path, sizeof(path), canonical, PATH_SIZE); + + EXPECT_STREQ(canonical, result); + EXPECT_EQ((size_t)size, (size_t)sizeof(result)); + EXPECT_EQ((char)'\0', (char)result[size - 1]); +} + +TEST_F(portability_path_test, portability_path_test_compare_root_equal) +{ + static const char left[] = "/"; + static const char right[] = "/"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_equal) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "/a/b/c"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_equal_trailing_slash_inverted) +{ + static const char left[] = "/a/b/c/"; + static const char right[] = "/a/b/c"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_equal_trailing_slash) +{ + static const char left[] = "/a/b/c"; + static const char right[] = "/a/b/c/"; + + EXPECT_EQ((int)0, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_root_empty) +{ + static const char left[] = "/"; + static const char right[] = ""; + + EXPECT_EQ((int)1, (int)portability_path_compare(left, right)); +} + +TEST_F(portability_path_test, portability_path_test_compare_root_file) +{ + static const char left[] = "/a"; + static const char right[] = "/"; + + EXPECT_EQ((int)1, (int)portability_path_compare(left, right)); +} diff --git a/source/tests/preprocessor_test/CMakeLists.txt b/source/tests/preprocessor_test/CMakeLists.txt index f6533c008a..b96a639036 100644 --- a/source/tests/preprocessor_test/CMakeLists.txt +++ b/source/tests/preprocessor_test/CMakeLists.txt @@ -102,11 +102,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/preprocessor_test/source/main.cpp b/source/tests/preprocessor_test/source/main.cpp index 8f29968341..ccd0ee35a6 100644 --- a/source/tests/preprocessor_test/source/main.cpp +++ b/source/tests/preprocessor_test/source/main.cpp @@ -2,7 +2,7 @@ * Logger Library by Parra Studios * A generic logger library providing application execution reports. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/preprocessor_test/source/preprocessor_test.cpp b/source/tests/preprocessor_test/source/preprocessor_test.cpp index f2ece41b92..4e406c3849 100644 --- a/source/tests/preprocessor_test/source/preprocessor_test.cpp +++ b/source/tests/preprocessor_test/source/preprocessor_test.cpp @@ -2,7 +2,7 @@ * Preprocssor Library by Parra Studios * A generic header-only preprocessor metaprogramming library. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,26 +18,24 @@ * */ -#include +#include #include -#include - class preprocessor_test : public testing::Test { - public: +public: }; TEST_F(preprocessor_test, arguments_comma) { - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_COMMA(1, 2, 3, 4, 5)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_COMMA(1, 2, 3, 4, 5)); - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_COMMA(1, 2)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_COMMA(1, 2)); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COMMA(1)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COMMA(1)); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COMMA()); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COMMA()); } TEST_F(preprocessor_test, arguments) @@ -46,33 +44,33 @@ TEST_F(preprocessor_test, arguments) size_t second_remove[] = { PREPROCESSOR_ARGS_SECOND_REMOVE(1, 2, 3, 4) }; - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_FIRST(1, 2, 3, 4, 5, 6)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_FIRST(1, 2, 3, 4, 5, 6)); - EXPECT_EQ((size_t) first_remove[0], (size_t) 2); + EXPECT_EQ((size_t)first_remove[0], (size_t)2); - EXPECT_EQ((size_t) first_remove[1], (size_t) 3); + EXPECT_EQ((size_t)first_remove[1], (size_t)3); - EXPECT_EQ((size_t) first_remove[2], (size_t) 4); + EXPECT_EQ((size_t)first_remove[2], (size_t)4); - EXPECT_EQ((int) 2, (int) PREPROCESSOR_ARGS_SECOND(1, 2, 3, 4, 5, 6)); + EXPECT_EQ((int)2, (int)PREPROCESSOR_ARGS_SECOND(1, 2, 3, 4, 5, 6)); - EXPECT_EQ((size_t) second_remove[0], (size_t) 1); + EXPECT_EQ((size_t)second_remove[0], (size_t)1); - EXPECT_EQ((size_t) second_remove[1], (size_t) 3); + EXPECT_EQ((size_t)second_remove[1], (size_t)3); - EXPECT_EQ((size_t) second_remove[2], (size_t) 4); + EXPECT_EQ((size_t)second_remove[2], (size_t)4); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COUNT()); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COUNT()); - EXPECT_EQ((int) 1, (int) PREPROCESSOR_ARGS_COUNT(a)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_COUNT(a)); - EXPECT_EQ((int) 2, (int) PREPROCESSOR_ARGS_COUNT(a, b)); + EXPECT_EQ((int)2, (int)PREPROCESSOR_ARGS_COUNT(a, b)); - EXPECT_EQ((int) 3, (int) PREPROCESSOR_ARGS_COUNT(a, b, c)); + EXPECT_EQ((int)3, (int)PREPROCESSOR_ARGS_COUNT(a, b, c)); - EXPECT_EQ((int) 4, (int) PREPROCESSOR_ARGS_COUNT(a, b, c, d)); + EXPECT_EQ((int)4, (int)PREPROCESSOR_ARGS_COUNT(a, b, c, d)); - EXPECT_EQ((int) 0, (int) PREPROCESSOR_ARGS_COUNT(/* ... */)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_ARGS_COUNT(/* ... */)); } TEST_F(preprocessor_test, bit) @@ -114,75 +112,149 @@ TEST_F(preprocessor_test, empty) { const char empty_str[] = ""; - EXPECT_EQ((int) 0, (int) strcmp(empty_str, PREPROCESSOR_STRINGIFY(PREPROCESSOR_EMPTY_SYMBOL()))); + EXPECT_STREQ("", PREPROCESSOR_STRINGIFY_OR_EMPTY(PREPROCESSOR_EMPTY_SYMBOL())); + EXPECT_STREQ(empty_str, PREPROCESSOR_STRINGIFY_OR_EMPTY(PREPROCESSOR_EMPTY_SYMBOL())); PREPROCESSOR_EMPTY_EXPANSION(this must compile) PREPROCESSOR_EMPTY_EXPANSION_VARIADIC(this, must, compile) } -TEST_F(preprocessor_test, for) +TEST_F(preprocessor_test, foreach) { - #define PREPROCESSOR_TEST_FOR_EACH_STR_SIZE 0x04 +#define PREPROCESSOR_TEST_FOR_EACH_STR_SIZE 0x04 char for_each_str[PREPROCESSOR_TEST_FOR_EACH_STR_SIZE]; - for_each_str[0] = '\0'; + memset(for_each_str, '\0', sizeof(char) * PREPROCESSOR_TEST_FOR_EACH_STR_SIZE); - #define PREPROCESSOR_TEST_FOR_EACH(expr) strncat(for_each_str, expr, PREPROCESSOR_TEST_FOR_EACH_STR_SIZE); +#define PREPROCESSOR_TEST_FOR_EACH(expr) strncat(for_each_str, expr, PREPROCESSOR_TEST_FOR_EACH_STR_SIZE - 1); PREPROCESSOR_FOR_EACH(PREPROCESSOR_TEST_FOR_EACH, "a", "b", "c") - #undef PREPROCESSOR_TEST_FOR_EACH +#undef PREPROCESSOR_TEST_FOR_EACH + +#undef PREPROCESSOR_TEST_FOR_EACH_STR_SIZE + + EXPECT_STREQ(for_each_str, "abc"); +} + +TEST_F(preprocessor_test, foreach_empty) +{ +#define PREPROCESSOR_TEST_FOR_EACH(expr) "33434" + + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_EMPTY(PREPROCESSOR_FOR_EACH(PREPROCESSOR_TEST_FOR_EACH))); + +#undef PREPROCESSOR_TEST_FOR_EACH +} - #undef PREPROCESSOR_TEST_FOR_EACH_STR_SIZE +TEST_F(preprocessor_test, for_empty) +{ +#define PREPROCESSOR_TEST_FOR(context, iterator, element) "33434" - EXPECT_EQ((int) 0, (int) strcmp(for_each_str, "abc")); + EXPECT_EQ((int)1, (int)PREPROCESSOR_ARGS_EMPTY(PREPROCESSOR_FOR(PREPROCESSOR_TEST_FOR, "yeet"))); + +#undef PREPROCESSOR_TEST_FOR +} + +TEST_F(preprocessor_test, for) +{ +#define PREPROCESSOR_TEST_FOR(context, iterator, element) \ + context \ + PREPROCESSOR_STRINGIFY(iterator) \ + element \ + "-" + + const char for_str[] = PREPROCESSOR_FOR(PREPROCESSOR_TEST_FOR, "yeet", "a", "b", "c", "d", "e", "f"); + +#undef PREPROCESSOR_TEST_FOR + + EXPECT_STREQ(for_str, "yeet0a-yeet1b-yeet2c-yeet3d-yeet4e-yeet5f-"); } TEST_F(preprocessor_test, if) { - EXPECT_EQ((int) 0, PREPROCESSOR_IF(1, (int) 0, (int) 1)); + EXPECT_EQ((int)0, PREPROCESSOR_IF(1, (int)0, (int)1)); + + EXPECT_NE((int)0, PREPROCESSOR_IF(0, (int)0, (int)1)); +} - EXPECT_NE((int) 0, PREPROCESSOR_IF(0, (int) 0, (int) 1)); +TEST_F(preprocessor_test, if_va_args) +{ +#define PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, ...) \ + PREPROCESSOR_IF(PREPROCESSOR_ARGS_EMPTY(__VA_ARGS__), 1, 0) + + EXPECT_EQ((int)1, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, D)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, D, E)); + EXPECT_EQ((int)0, (int)PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, D, E, F)); + +#undef PREPROCESSOR_TEST_IF_VA_ARGS +} + +TEST_F(preprocessor_test, if_va_args_ext) +{ +#define PREPROCESSOR_TEST_IF_VA_ARGS_PRINT(A, B, C) \ + do \ + { \ + printf("%d %d %d\n", A, B, C); \ + } while (0) + +#define PREPROCESSOR_TEST_IF_VA_ARGS_PRINT_VA(A, B, C, ...) \ + do \ + { \ + printf(A, B, C, __VA_ARGS__); \ + } while (0) + +#define PREPROCESSOR_TEST_IF_VA_ARGS(A, B, C, ...) \ + PREPROCESSOR_IF(PREPROCESSOR_ARGS_EMPTY(__VA_ARGS__), \ + PREPROCESSOR_TEST_IF_VA_ARGS_PRINT(A, B, C), \ + PREPROCESSOR_TEST_IF_VA_ARGS_PRINT_VA(A, B, C, __VA_ARGS__)) + + PREPROCESSOR_TEST_IF_VA_ARGS(1, 2, 3); + PREPROCESSOR_TEST_IF_VA_ARGS("%s %s %s\n", "B", "C", "D"); + PREPROCESSOR_TEST_IF_VA_ARGS("%s %s %s %s\n", "B", "C", "D", "E"); + PREPROCESSOR_TEST_IF_VA_ARGS("%s %s %s %s %s\n", "B", "C", "D", "E", "F"); + +#undef PREPROCESSOR_TEST_IF_VA_ARGS } TEST_F(preprocessor_test, serial) { - #define PREPROCSSOR_TEST_SERIAL_TAG abc +#define PREPROCSSOR_TEST_SERIAL_TAG abc const char serial_id_a[] = PREPROCESSOR_STRINGIFY(PREPROCESSOR_SERIAL_ID(PREPROCESSOR_TEST_SERIAL_TAG)); const char serial_id_b[] = PREPROCESSOR_STRINGIFY(PREPROCESSOR_SERIAL_ID(PREPROCESSOR_TEST_SERIAL_TAG)); - EXPECT_NE((int) 0, (int) strcmp(serial_id_a, serial_id_b)); + EXPECT_STRNE(serial_id_a, serial_id_b); - #undef PREPROCESSOR_TEST_SERIAL_TAG +#undef PREPROCESSOR_TEST_SERIAL_TAG } TEST_F(preprocessor_test, stringify) { - #define PREPROCESSOR_TEST_STRINGIFY_TAG abc +#define PREPROCESSOR_TEST_STRINGIFY_TAG abc const char stringify_tag[] = "abc"; - EXPECT_EQ((int) 0, (int) strcmp(stringify_tag, PREPROCESSOR_STRINGIFY(PREPROCESSOR_TEST_STRINGIFY_TAG))); + EXPECT_STREQ(stringify_tag, PREPROCESSOR_STRINGIFY(PREPROCESSOR_TEST_STRINGIFY_TAG)); - EXPECT_EQ((int) 0, (int) strcmp(stringify_tag, PREPROCESSOR_STRINGIFY_VARIADIC(a, b, c))); + EXPECT_STREQ(stringify_tag, PREPROCESSOR_STRINGIFY_VARIADIC(a, b, c)); - #undef PREPROCESSOR_TEST_STRINGIFY_TAG +#undef PREPROCESSOR_TEST_STRINGIFY_TAG } TEST_F(preprocessor_test, tuple) { - EXPECT_EQ((int) 0, PREPROCESSOR_TUPLE_EXPAND(PREPROCESSOR_TUPLE_MAKE((int) 0))); + EXPECT_EQ((int)0, PREPROCESSOR_TUPLE_EXPAND(PREPROCESSOR_TUPLE_MAKE((int)0))); - PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_EQ, PREPROCESSOR_TUPLE_PREPEND((int) 1, PREPROCESSOR_TUPLE_MAKE((int) 1))); + PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_EQ, PREPROCESSOR_TUPLE_PREPEND((int)1, PREPROCESSOR_TUPLE_MAKE((int)1))); - PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_NE, PREPROCESSOR_TUPLE_APPEND((int) 1, PREPROCESSOR_TUPLE_MAKE((int) 0))); + PREPROCESSOR_TUPLE_EXPAND_MACRO(EXPECT_NE, PREPROCESSOR_TUPLE_APPEND((int)1, PREPROCESSOR_TUPLE_MAKE((int)0))); } TEST_F(preprocessor_test, if_with_arguments_count) { - EXPECT_EQ((int) 1, (int) PREPROCESSOR_IF(PREPROCESSOR_ARGS_COUNT(a, b, c), 1, 0)); + EXPECT_EQ((int)1, (int)PREPROCESSOR_IF(PREPROCESSOR_ARGS_COUNT(a, b, c), 1, 0)); } diff --git a/source/tests/py_django_integration_test/CMakeLists.txt b/source/tests/py_django_integration_test/CMakeLists.txt deleted file mode 100644 index 5f5d859c94..0000000000 --- a/source/tests/py_django_integration_test/CMakeLists.txt +++ /dev/null @@ -1,234 +0,0 @@ -# Check if python loader and distributable libs are enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - -# -# External dependencies -# - -find_package(PythonLibs 3 REQUIRED) - -if(NOT PYTHONLIBS_FOUND) - message(STATUS "Python libraries not found") - return() -endif() - -set(Python_ADDITIONAL_VERSIONS 3.5) - -find_package(PythonInterp REQUIRED) - -if(NOT PYTHONINTERP_FOUND) - message(STATUS "Python interpreter not found") - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target py-django-integration-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/py_django_integration_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Dependecies -# - -# Add metacall distributable dependency -add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable -) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - get_target_property(DEBUG_POSTFIX ${target} "DEBUG_POSTFIX") - set(TEST_SCRIPT_LAUNCHER_BASE_NAME - ${target}${DEBUG_POSTFIX} - ) -else() - set(TEST_SCRIPT_LAUNCHER_BASE_NAME - ${target} - ) -endif() - -set(TEST_SCRIPT_LAUNCHER_NAME - ${TEST_SCRIPT_LAUNCHER_BASE_NAME}.py -) - -set(TEST_SCRIPT_LAUNCHER_CLIENT_NAME - ${TEST_SCRIPT_LAUNCHER_BASE_NAME}-client.py -) - -set(TEST_SCRIPT_LAUNCHER_FILE_NAME - ${TEST_SCRIPT_LAUNCHER_BASE_NAME}-file.txt -) - -set(TEST_SCRIPT_LAUNCHER_NAME_IN - "data/test.py.in" -) - -set(TEST_SCRIPT_LAUNCHER_CLIENT_NAME_IN - "data/client.py.in" -) - -set(TEST_SCRIPT_LAUNCHER_FILE_NAME_IN - "data/django_file.txt" -) - -# -# Configure test files -# - -if(MSVC) - set(DETOUR_LIBRARY_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - set(SERIAL_LIBRARY_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - set(LOADER_LIBRARY_PATH "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}") - set(TEST_SCRIPT_LAUNCHER_APP "${PROJECT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${TEST_SCRIPT_LAUNCHER_BASE_NAME}.exe") -else() - set(DETOUR_LIBRARY_PATH "${PROJECT_BINARY_DIR}") - set(SERIAL_LIBRARY_PATH "${PROJECT_BINARY_DIR}") - set(LOADER_LIBRARY_PATH "${PROJECT_BINARY_DIR}") - set(TEST_SCRIPT_LAUNCHER_APP "${PROJECT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_BASE_NAME}") -endif() - -configure_file(${TEST_SCRIPT_LAUNCHER_NAME_IN} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_NAME}) - -configure_file(${TEST_SCRIPT_LAUNCHER_CLIENT_NAME_IN} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_CLIENT_NAME}) - -configure_file(${TEST_SCRIPT_LAUNCHER_FILE_NAME_IN} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_FILE_NAME} COPYONLY) - -if(NOT ${PYTHON_VERSION_MAJOR} EQUAL 3) - message(WARNING "Invalid Python executable version: ${PYTHON_VERSION_STRING}") - message(WARNING "Define a valid Python 3 executable in variable PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} or ${target} test won't be compiled") - message(WARNING "Use: \"sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10\" to modify default python executable") - return() -endif() - -add_test(NAME ${target} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_SCRIPT_LAUNCHER_NAME} -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/py_django_integration_test/data/client.py.in b/source/tests/py_django_integration_test/data/client.py.in deleted file mode 100755 index 7498304959..0000000000 --- a/source/tests/py_django_integration_test/data/client.py.in +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/python3 - -import unittest -import time -import urllib.request -import requests -import signal -import os - -class py_django_integration_client_test(unittest.TestCase): - def test_py_django_client(self): - - print('Client: Waiting for python django server ...'); - - time.sleep(3); # Fake reactive behavior - - print('Client: Sending GET request to index'); - - request = urllib.request.urlopen('http://127.0.0.1:8080/').read(); - - print('Client: Request { ' + str(request) + ' }'); - - self.assertEqual(request, b'hello world'); - - print('Client: Sending POST request to media'); - - file_handle = open('${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_FILE_NAME@'); - - files = { 'file': file_handle }; - - request = requests.post('http://127.0.0.1:8080/media/', files = files); - - file_handle.close(); - - print('Client: Request { ' + str(request) + ' }'); - - self.assertEqual(request.content, b'123456'); - - print('Client: Killing python django server (${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_BASE_NAME@)'); - - request = urllib.request.urlopen('http://127.0.0.1:8080/pid/').read(); - - print('Client: Request { ' + str(request) + ' }'); - - os.kill(int(request), signal.SIGINT); - - print('Server: Python django server killed gracefully'); - -if __name__ == '__main__': - unittest.main() diff --git a/source/tests/py_django_integration_test/data/django_file.txt b/source/tests/py_django_integration_test/data/django_file.txt deleted file mode 100644 index 4632e068d5..0000000000 --- a/source/tests/py_django_integration_test/data/django_file.txt +++ /dev/null @@ -1 +0,0 @@ -123456 \ No newline at end of file diff --git a/source/tests/py_django_integration_test/data/test.py.in b/source/tests/py_django_integration_test/data/test.py.in deleted file mode 100755 index ac9801d741..0000000000 --- a/source/tests/py_django_integration_test/data/test.py.in +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python3 - - -import os - -os.environ['DETOUR_LIBRARY_PATH'] = '@DETOUR_LIBRARY_PATH@' -os.environ['SERIAL_LIBRARY_PATH'] = '@SERIAL_LIBRARY_PATH@' -os.environ['CONFIGURATION_PATH'] = '@CONFIGURATION_PATH@' -os.environ['LOADER_LIBRARY_PATH'] = '@LOADER_LIBRARY_PATH@' -os.environ['LOADER_SCRIPT_PATH'] = '@LOADER_SCRIPT_PATH@' - -import unittest -import signal -import subprocess - -def sigint_handler(signal, frame): - print('Server: SIGINT signal from client to server (Ctrl-C)'); - - -class py_django_integration_test(unittest.TestCase): - - def test_py_django_server(self): - - signal.signal(signal.SIGINT, sigint_handler); - - print('Server: Launching python django client in background (${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_CLIENT_NAME@)'); - - django_client = subprocess.Popen(['${CMAKE_CURRENT_BINARY_DIR}/@TEST_SCRIPT_LAUNCHER_CLIENT_NAME@']); - - print('Server: Launching python django server (@TEST_SCRIPT_LAUNCHER_APP@)'); - - django_server = subprocess.Popen(['@TEST_SCRIPT_LAUNCHER_APP@'], env = os.environ.copy(), stderr = subprocess.STDOUT); - - django_client_output = django_client.communicate()[0]; - - print(django_client_output); - - self.assertEqual(django_client.poll(), 0); - - print('Server: Python django client finished gracefully'); - -if __name__ == '__main__': - unittest.main() diff --git a/source/tests/py_django_integration_test/source/main.cpp b/source/tests/py_django_integration_test/source/main.cpp deleted file mode 100644 index 0cbc70d36b..0000000000 --- a/source/tests/py_django_integration_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/py_django_integration_test/source/py_django_integration_test.cpp b/source/tests/py_django_integration_test/source/py_django_integration_test.cpp deleted file mode 100644 index 51c0335233..0000000000 --- a/source/tests/py_django_integration_test/source/py_django_integration_test.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -class py_django_integration_test : public testing::Test -{ -public: -}; - -TEST_F(py_django_integration_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - { - const char * py_scripts[] = - { - "manage.py" - }; - - void * ret = NULL; - - ASSERT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - - ret = metacall("frontend_initialize", 8080); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((long) 0, (long) metacall_value_to_long(ret)); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_PY */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/py_loader_port_test/CMakeLists.txt b/source/tests/py_loader_port_test/CMakeLists.txt deleted file mode 100644 index f6cb38a5f4..0000000000 --- a/source/tests/py_loader_port_test/CMakeLists.txt +++ /dev/null @@ -1,155 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_SCRIPTS_RB OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY) - return() -endif() - -# -# External dependencies -# - -# MetaCall Library -find_package(MetaCall REQUIRED) - -if(NOT METACALL_FOUND) - message(STATUS "MetaCall libraries not found") - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target py-loader-port-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/py_loader_port_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include - - ${METACALL_INCLUDE_DIRS} # MetaCall includes -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${CMAKE_DL_LIBS} - - ${METACALL_LIBRARIES} # MetaCall libraries -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/py_loader_port_test/source/main.cpp b/source/tests/py_loader_port_test/source/main.cpp deleted file mode 100644 index ca664715dc..0000000000 --- a/source/tests/py_loader_port_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading python code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/py_loader_port_test/source/py_loader_port_test.cpp b/source/tests/py_loader_port_test/source/py_loader_port_test.cpp deleted file mode 100644 index 4b852d9853..0000000000 --- a/source/tests/py_loader_port_test/source/py_loader_port_test.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading python code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -class py_loader_port_test : public testing::Test -{ -public: -}; - -void * callback_host(void * args[]) -{ - const char * str = metacall_value_cast_string(&args[0]); - - printf("Host callback: %s\n", str); - - EXPECT_EQ((int) 0, (int) strcmp(str, "some text")); - - return metacall_value_create_int(25); -} - -TEST_F(py_loader_port_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Native register */ - { - metacall_register("callback_host", callback_host, METACALL_INT, 1, METACALL_STRING); - - EXPECT_NE((void *) NULL, (void *) metacall_function("callback_host")); - } - - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) - { - const char * rb_scripts[] = - { - "hello.rb", "second.rb" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - - ret = metacall("say_multiply", 5, 7); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 35, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - - EXPECT_EQ((void *) NULL, (void *) metacall("say_null")); - - ret = metacall("say_hello", "meta-programmer"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_cast_string(&ret), "Hello meta-programmer!")); - - metacall_value_destroy(ret); - - ret = metacall("get_second", 5, 12); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 12, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_RB */ - - /* Python */ - #if defined(OPTION_BUILD_LOADERS_PY) - { - const char * py_scripts[] = - { - "callback.py" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); - - ret = metacall("hello_world", "some text"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 25, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - - ret = metacall("hello_ruby", 12, 4); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 48, (int) metacall_value_cast_int(&ret)); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_PY */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/py_loader_test/CMakeLists.txt b/source/tests/py_loader_test/CMakeLists.txt deleted file mode 100644 index 401e655a09..0000000000 --- a/source/tests/py_loader_test/CMakeLists.txt +++ /dev/null @@ -1,151 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target py-loader-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/py_loader_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/py_loader_test/source/main.cpp b/source/tests/py_loader_test/source/main.cpp deleted file mode 100644 index ca664715dc..0000000000 --- a/source/tests/py_loader_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading python code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/py_loader_test/source/py_loader_test.cpp b/source/tests/py_loader_test/source/py_loader_test.cpp deleted file mode 100644 index c1e7c975cb..0000000000 --- a/source/tests/py_loader_test/source/py_loader_test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading python code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class py_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(py_loader_test, DefaultConstructor) -{ - const loader_naming_path names[] = - { - "example.py" - }; - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, loader_load_from_file("py", names, sizeof(names) / sizeof(names[0]), NULL)); - - EXPECT_EQ((int) 0, loader_unload()); -} diff --git a/source/tests/rb_loader_parser_integration_test/CMakeLists.txt b/source/tests/rb_loader_parser_integration_test/CMakeLists.txt deleted file mode 100644 index e6686cf86e..0000000000 --- a/source/tests/rb_loader_parser_integration_test/CMakeLists.txt +++ /dev/null @@ -1,152 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target rb-loader-parser-integration-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/rb_loader_parser_integration_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::detour - ${META_PROJECT_NAME}::serial - ${META_PROJECT_NAME}::configuration - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::metacall -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/rb_loader_parser_integration_test/source/main.cpp b/source/tests/rb_loader_parser_integration_test/source/main.cpp deleted file mode 100644 index 0cbc70d36b..0000000000 --- a/source/tests/rb_loader_parser_integration_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/rb_loader_parser_integration_test/source/rb_loader_parser_integration_test.cpp b/source/tests/rb_loader_parser_integration_test/source/rb_loader_parser_integration_test.cpp deleted file mode 100644 index 4f3e07db0d..0000000000 --- a/source/tests/rb_loader_parser_integration_test/source/rb_loader_parser_integration_test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -#include - -class rb_loader_parser_integration_test : public testing::Test -{ -public: -}; - -TEST_F(rb_loader_parser_integration_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) - { - const char * rb_scripts[] = - { - "cache.rb" - }; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]), NULL)); - - EXPECT_EQ((void *) NULL, (void *) metacall("cache_initialize")); - - EXPECT_EQ((void *) NULL, (void *) metacall("cache_set", "meta", "call")); - - ret = metacall("cache_has_key","meta"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_NE((int) 0, (int) metacall_value_to_bool(ret)); - - metacall_value_destroy(ret); - - ret = metacall("cache_get", "meta"); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 0, (int) strcmp(metacall_value_to_string(ret), "call")); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_RB */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/rb_loader_parser_test/CMakeLists.txt b/source/tests/rb_loader_parser_test/CMakeLists.txt index a48e43aebd..af3df47fee 100644 --- a/source/tests/rb_loader_parser_test/CMakeLists.txt +++ b/source/tests/rb_loader_parser_test/CMakeLists.txt @@ -33,6 +33,10 @@ set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") set(sources ${source_path}/main.cpp ${source_path}/rb_loader_parser_test.cpp + + # Add directly the sources to be tested, Ruby Loader is a MODULE and it cannot be linked + ${CMAKE_SOURCE_DIR}/source/loaders/rb_loader/include/rb_loader/rb_loader_impl_parser.h + ${CMAKE_SOURCE_DIR}/source/loaders/rb_loader/source/rb_loader_impl_parser.c ) # Group source files @@ -73,6 +77,10 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + # Ruby Loader headers + ${CMAKE_BINARY_DIR}/source/loaders/rb_loader/include + ${CMAKE_SOURCE_DIR}/source/loaders/rb_loader/include ) # @@ -87,10 +95,13 @@ target_link_libraries(${target} ${META_PROJECT_NAME}::version ${META_PROJECT_NAME}::preprocessor + ${META_PROJECT_NAME}::environment ${META_PROJECT_NAME}::format + ${META_PROJECT_NAME}::threading ${META_PROJECT_NAME}::log + ${META_PROJECT_NAME}::memory + ${META_PROJECT_NAME}::portability ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::rb_loader ) # @@ -111,11 +122,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -128,6 +148,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + rb_loader +) + # # Define test properties # diff --git a/source/tests/rb_loader_parser_test/source/main.cpp b/source/tests/rb_loader_parser_test/source/main.cpp index cdb6abc837..37d4adc23f 100644 --- a/source/tests/rb_loader_parser_test/source/main.cpp +++ b/source/tests/rb_loader_parser_test/source/main.cpp @@ -2,7 +2,7 @@ * Loader Library by Parra Studios * A plugin for loading ruby code at run-time into a process. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp b/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp index 402f0e964d..a8984ff47f 100644 --- a/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp +++ b/source/tests/rb_loader_parser_test/source/rb_loader_parser_test.cpp @@ -1,12 +1,12 @@ /* * Loader Library by Parra Studios - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * A plugin for loading ruby code at run-time into a process. * */ -#include +#include #include @@ -14,7 +14,7 @@ class rb_loader_parser_test : public testing::Test { - protected: +protected: }; TEST_F(rb_loader_parser_test, DefaultConstructor) @@ -66,17 +66,17 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) rb_function_parser function_parser; - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); function_map = set_create(&hash_callback_str, &comparable_callback_str); - EXPECT_NE((set) NULL, (set) function_map); + EXPECT_NE((set)NULL, (set)function_map); - EXPECT_EQ((int) 0, (int) rb_loader_impl_key_parse(script, function_map)); + EXPECT_EQ((int)0, (int)rb_loader_impl_key_parse(script, function_map)); rb_loader_impl_key_print(function_map); @@ -86,15 +86,15 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_nothing); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_nothing, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_nothing, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 1, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)1, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_get */ @@ -103,15 +103,15 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_get); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_get, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_get, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 1, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)1, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_set */ @@ -120,19 +120,19 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_set); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_set, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_set, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 2, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)2, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); - EXPECT_EQ((int) 1, (int) function_parser->params[1].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[1].name, "value", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[1].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)1, (int)function_parser->params[1].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[1].name, "value", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[1].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_has_key */ @@ -141,15 +141,15 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_has_key); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_has_key, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_has_key, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 1, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)1, (size_t)function_parser->params_size); - EXPECT_EQ((int) 0, (int) function_parser->params[0].index); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); + EXPECT_EQ((int)0, (int)function_parser->params[0].index); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].name, "key", RB_LOADER_IMPL_PARSER_KEY)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->params[0].type, "String", RB_LOADER_IMPL_PARSER_TYPE)); } /* cache_initialize */ @@ -158,14 +158,12 @@ TEST_F(rb_loader_parser_test, DefaultConstructor) function_parser = (rb_function_parser)set_get(function_map, (set_key)cache_initialize); - EXPECT_NE((rb_function_parser) NULL, (rb_function_parser) function_parser); + EXPECT_NE((rb_function_parser)NULL, (rb_function_parser)function_parser); - EXPECT_EQ((int) 0, (int) strncmp(function_parser->name, cache_initialize, RB_LOADER_IMPL_PARSER_FUNC)); + EXPECT_EQ((int)0, (int)strncmp(function_parser->name, cache_initialize, RB_LOADER_IMPL_PARSER_FUNC)); - EXPECT_EQ((size_t) 0, (size_t) function_parser->params_size); + EXPECT_EQ((size_t)0, (size_t)function_parser->params_size); } - EXPECT_EQ((int) 0, (int) rb_loader_impl_key_clear(function_map)); - - set_destroy(function_map); + rb_loader_impl_key_clear(function_map); } diff --git a/source/tests/rb_loader_test/CMakeLists.txt b/source/tests/rb_loader_test/CMakeLists.txt deleted file mode 100644 index f3eba4db23..0000000000 --- a/source/tests/rb_loader_test/CMakeLists.txt +++ /dev/null @@ -1,145 +0,0 @@ -# Check if this loader is enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target rb-loader-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/rb_loader_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::loader - ${META_PROJECT_NAME}::configuration -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/rb_loader_test/source/main.cpp b/source/tests/rb_loader_test/source/main.cpp deleted file mode 100644 index cdb6abc837..0000000000 --- a/source/tests/rb_loader_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/rb_loader_test/source/rb_loader_test.cpp b/source/tests/rb_loader_test/source/rb_loader_test.cpp deleted file mode 100644 index d13ae233a1..0000000000 --- a/source/tests/rb_loader_test/source/rb_loader_test.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Loader Library by Parra Studios - * A plugin for loading ruby code at run-time into a process. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -class rb_loader_test : public testing::Test -{ - protected: -}; - -TEST_F(rb_loader_test, DefaultConstructor) -{ - const loader_naming_path names[] = - { - "hello.rb" - }; - - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); - - EXPECT_EQ((int) 0, (int) loader_load_from_file("rb", names, sizeof(names) / sizeof(names[0]), NULL)); - - EXPECT_EQ((int) 0, (int) loader_unload()); -} diff --git a/source/tests/rb_rails_integration_test/CMakeLists.txt b/source/tests/rb_rails_integration_test/CMakeLists.txt deleted file mode 100644 index d69777c116..0000000000 --- a/source/tests/rb_rails_integration_test/CMakeLists.txt +++ /dev/null @@ -1,155 +0,0 @@ -# -# Setup distributable environment -# - -# Check if ruby loader and distributable libs are enabled -if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_RB OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_RB OR NOT OPTION_BUILD_DIST_LIBS) - return() -endif() - -# -# Executable name and options -# - -# Target name -set(target rb-rails-integration-test) -message(STATUS "Test ${target}") - -# -# Compiler warnings -# - -include(Warnings) - -# -# Compiler security -# - -include(SecurityFlags) - -# -# Sources -# - -set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(sources - ${source_path}/main.cpp - ${source_path}/rb_rails_integration_test.cpp -) - -# Group source files -set(header_group "Header Files (API)") -set(source_group "Source Files") -source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" - ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) - -# -# Create executable -# - -# Build executable -add_executable(${target} - ${sources} -) - -# Create namespaced alias -add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) - -# -# Dependecies -# - -# Add metacall distributable dependency -add_dependencies(${target} - ${META_PROJECT_NAME}::metacall_distributable -) - -# -# Project options -# - -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - FOLDER "${IDE_FOLDER}" -) - -# -# Include directories -# - -target_include_directories(${target} - PRIVATE - ${DEFAULT_INCLUDE_DIRECTORIES} - ${PROJECT_BINARY_DIR}/source/include -) - -# -# Libraries -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LIBRARIES} - - GTest - - ${CMAKE_DL_LIBS} - - ${META_PROJECT_NAME}::metacall_distributable -) - -# -# Compile definitions -# - -target_compile_definitions(${target} - PRIVATE - ${DEFAULT_COMPILE_DEFINITIONS} -) - -# -# Compile options -# - -target_compile_options(${target} - PRIVATE - ${DEFAULT_COMPILE_OPTIONS} -) - -# -# Linker options -# - -target_link_libraries(${target} - PRIVATE - ${DEFAULT_LINKER_OPTIONS} -) - -# -# Define test -# - -add_test(NAME ${target} - COMMAND $ - WORKING_DIRECTORY ${LOADER_SCRIPT_PATH} -) - -# -# Define test properties -# - -set_property(TEST ${target} - PROPERTY LABELS ${target} -) - -include(TestEnvironmentVariables) - -test_environment_variables(${target} - "" - ${TESTS_ENVIRONMENT_VARIABLES} -) diff --git a/source/tests/rb_rails_integration_test/source/main.cpp b/source/tests/rb_rails_integration_test/source/main.cpp deleted file mode 100644 index 0cbc70d36b..0000000000 --- a/source/tests/rb_rails_integration_test/source/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -int main(int argc, char * argv[]) -{ - ::testing::InitGoogleMock(&argc, argv); - - return RUN_ALL_TESTS(); -} diff --git a/source/tests/rb_rails_integration_test/source/rb_rails_integration_test.cpp b/source/tests/rb_rails_integration_test/source/rb_rails_integration_test.cpp deleted file mode 100644 index ae27cea564..0000000000 --- a/source/tests/rb_rails_integration_test/source/rb_rails_integration_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * MetaCall Library by Parra Studios - * A library for providing a foreign function interface calls. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -class rb_rails_integration_test : public testing::Test -{ -public: -}; - -TEST_F(rb_rails_integration_test, DefaultConstructor) -{ - metacall_print_info(); - - ASSERT_EQ((int) 0, (int) metacall_initialize()); - - /* Ruby */ - #if defined(OPTION_BUILD_LOADERS_RB) - { - const char * rb_scripts[] = - { - "blog.rb" - }; - - const char func_run[] = "run_and_kill_server"; - - void * ret = NULL; - - EXPECT_EQ((int) 0, (int) metacall_load_from_file("rb", rb_scripts, sizeof(rb_scripts) / sizeof(rb_scripts[0]))); - - EXPECT_NE((void *) NULL, (void *) metacall_function(func_run)); - - ret = metacall(func_run); - - EXPECT_NE((void *) NULL, (void *) ret); - - EXPECT_EQ((int) 0, (int) metacall_value_to_int(ret)); - - metacall_value_destroy(ret); - } - #endif /* OPTION_BUILD_LOADERS_RB */ - - EXPECT_EQ((int) 0, (int) metacall_destroy()); -} diff --git a/source/tests/reflect_function_test/CMakeLists.txt b/source/tests/reflect_function_test/CMakeLists.txt index 5cb2a0ad19..fd1875cd36 100644 --- a/source/tests/reflect_function_test/CMakeLists.txt +++ b/source/tests/reflect_function_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,16 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -110,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_function_test/source/main.cpp b/source/tests/reflect_function_test/source/main.cpp index 6123b85dbf..cba58797ca 100644 --- a/source/tests/reflect_function_test/source/main.cpp +++ b/source/tests/reflect_function_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_function_test/source/reflect_function_test.cpp b/source/tests/reflect_function_test/source/reflect_function_test.cpp index 085bbdd7d4..7567f4505c 100644 --- a/source/tests/reflect_function_test/source/reflect_function_test.cpp +++ b/source/tests/reflect_function_test/source/reflect_function_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * */ -#include +#include #include @@ -41,9 +41,9 @@ typedef struct function_impl_example_type } * function_impl_example; -void function_example(char c, int i, void * p) +void function_example(char c, int i, void *p) { - struct example_arg_type * e = (struct example_arg_type *)p; + struct example_arg_type *e = (struct example_arg_type *)p; log_write("metacall", LOG_LEVEL_DEBUG, "char: %c; int: %d; ptr: %p", c, i, p); @@ -66,17 +66,33 @@ int function_example_interface_create(function func, function_impl impl) return 1; } -function_return function_example_interface_invoke(function func, function_impl func_impl, function_args args) +function_return function_example_interface_invoke(function func, function_impl func_impl, function_args args, size_t size) { function_impl_example example_impl = (function_impl_example)func_impl; (void)func; + (void)size; example_impl->ptr(*((char *)(args[0])), *((int *)(args[1])), (void *)(args[2])); return NULL; } +function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void *context) +{ + /* TODO */ + + (void)func; + (void)impl; + (void)args; + (void)size; + (void)resolve_callback; + (void)reject_callback; + (void)context; + + return NULL; +} + void function_example_interface_destroy(function func, function_impl func_impl) { (void)func; @@ -86,10 +102,10 @@ void function_example_interface_destroy(function func, function_impl func_impl) function_interface function_example_singleton() { - static struct function_interface_type example_interface = - { + static struct function_interface_type example_interface = { &function_example_interface_create, &function_example_interface_invoke, + &function_example_interface_await, &function_example_interface_destroy }; @@ -98,24 +114,24 @@ function_interface function_example_singleton() class reflect_function_test : public testing::Test { - public: +public: }; TEST_F(reflect_function_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); type int_type = type_create(TYPE_INT, "int", NULL, NULL); type ptr_type = type_create(TYPE_PTR, "ptr", NULL, NULL); - EXPECT_NE((type) char_type, (type) NULL); - EXPECT_NE((type) int_type, (type) NULL); - EXPECT_NE((type) ptr_type, (type) NULL); + EXPECT_NE((type)char_type, (type)NULL); + EXPECT_NE((type)int_type, (type)NULL); + EXPECT_NE((type)ptr_type, (type)NULL); if (char_type != NULL && int_type != NULL && ptr_type != NULL) { @@ -129,6 +145,8 @@ TEST_F(reflect_function_test, DefaultConstructor) if (f != NULL) { + EXPECT_EQ((int)function_increment_reference(f), (int)0); + signature_set(function_signature(f), 0, "c", char_type); signature_set(function_signature(f), 1, "i", int_type); signature_set(function_signature(f), 2, "p", ptr_type); @@ -141,7 +159,7 @@ TEST_F(reflect_function_test, DefaultConstructor) function_args args = { &c, &i, &e }; - function_call(f, args); + function_call(f, args, sizeof(args) / sizeof(args[0])); } function_destroy(f); diff --git a/source/tests/reflect_metadata_test/CMakeLists.txt b/source/tests/reflect_metadata_test/CMakeLists.txt index 55d475ef32..33497b513c 100644 --- a/source/tests/reflect_metadata_test/CMakeLists.txt +++ b/source/tests/reflect_metadata_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,16 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -110,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_metadata_test/source/main.cpp b/source/tests/reflect_metadata_test/source/main.cpp index 6123b85dbf..cba58797ca 100644 --- a/source/tests/reflect_metadata_test/source/main.cpp +++ b/source/tests/reflect_metadata_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp b/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp index 7e668d0ae4..6c1bc10f7a 100644 --- a/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp +++ b/source/tests/reflect_metadata_test/source/reflect_metadata_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,10 @@ * */ -#include +#include #include +#include #include @@ -41,9 +42,9 @@ typedef struct function_impl_example_type } * function_impl_example; -void function_example(char c, int i, void * p) +void function_example(char c, int i, void *p) { - struct example_arg_type * e = (struct example_arg_type *)p; + struct example_arg_type *e = (struct example_arg_type *)p; log_write("metacall", LOG_LEVEL_DEBUG, "char: %c; int: %d; ptr: %p", c, i, p); @@ -66,17 +67,33 @@ int function_example_interface_create(function func, function_impl impl) return 1; } -function_return function_example_interface_invoke(function func, function_impl func_impl, function_args args) +function_return function_example_interface_invoke(function func, function_impl func_impl, function_args args, size_t size) { function_impl_example example_impl = (function_impl_example)func_impl; (void)func; + (void)size; example_impl->ptr(*((char *)(args[0])), *((int *)(args[1])), (void *)(args[2])); return NULL; } +function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void *context) +{ + /* TODO */ + + (void)func; + (void)impl; + (void)args; + (void)size; + (void)resolve_callback; + (void)reject_callback; + (void)context; + + return NULL; +} + void function_example_interface_destroy(function func, function_impl func_impl) { (void)func; @@ -86,10 +103,10 @@ void function_example_interface_destroy(function func, function_impl func_impl) function_interface function_example_singleton() { - static struct function_interface_type example_interface = - { + static struct function_interface_type example_interface = { &function_example_interface_create, &function_example_interface_invoke, + &function_example_interface_await, &function_example_interface_destroy }; @@ -98,32 +115,32 @@ function_interface function_example_singleton() class reflect_metadata_test : public testing::Test { - public: +public: }; TEST_F(reflect_metadata_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); // Initialize serial - EXPECT_EQ((int) 0, (int) serial_initialize()); + EXPECT_EQ((int)0, (int)serial_initialize()); // Create serial serial s = serial_create("rapid_json"); - ASSERT_NE((serial) NULL, (serial) s); + ASSERT_NE((serial)NULL, (serial)s); type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); type int_type = type_create(TYPE_INT, "int", NULL, NULL); type ptr_type = type_create(TYPE_PTR, "ptr", NULL, NULL); - EXPECT_NE((type) char_type, (type) NULL); - EXPECT_NE((type) int_type, (type) NULL); - EXPECT_NE((type) ptr_type, (type) NULL); + EXPECT_NE((type)char_type, (type)NULL); + EXPECT_NE((type)int_type, (type)NULL); + EXPECT_NE((type)ptr_type, (type)NULL); if (char_type != NULL && int_type != NULL && ptr_type != NULL) { @@ -131,13 +148,15 @@ TEST_F(reflect_metadata_test, DefaultConstructor) function_impl_example example_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); - char * str; + char *str; size_t size; f = function_create("example", 3, example_impl, &function_example_singleton); - EXPECT_NE((function) f, (function) NULL); + EXPECT_NE((function)f, (function)NULL); + + EXPECT_EQ((int)function_increment_reference(f), (int)0); if (f != NULL) { @@ -159,7 +178,7 @@ TEST_F(reflect_metadata_test, DefaultConstructor) function_args args = { &c, &i, &e }; - function_call(f, args); + function_call(f, args, sizeof(args) / sizeof(args[0])); } metadata = function_metadata(f); @@ -183,7 +202,7 @@ TEST_F(reflect_metadata_test, DefaultConstructor) } // Clear serial - EXPECT_EQ((int) 0, (int) serial_clear(s)); + EXPECT_EQ((int)0, (int)serial_clear(s)); // Destroy serial serial_destroy(); diff --git a/source/tests/reflect_object_class_test/CMakeLists.txt b/source/tests/reflect_object_class_test/CMakeLists.txt new file mode 100644 index 0000000000..3a74590dc5 --- /dev/null +++ b/source/tests/reflect_object_class_test/CMakeLists.txt @@ -0,0 +1,150 @@ +# +# Executable name and options +# + +# Target name +set(target reflect-object-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/reflect_object_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define test labels +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/reflect_object_class_test/source/main.cpp b/source/tests/reflect_object_class_test/source/main.cpp new file mode 100644 index 0000000000..cba58797ca --- /dev/null +++ b/source/tests/reflect_object_class_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Reflect Library by Parra Studios + * A library for provide reflection and metadata representation. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/reflect_object_class_test/source/reflect_object_class_test.cpp b/source/tests/reflect_object_class_test/source/reflect_object_class_test.cpp new file mode 100644 index 0000000000..1abd68347d --- /dev/null +++ b/source/tests/reflect_object_class_test/source/reflect_object_class_test.cpp @@ -0,0 +1,576 @@ +/* + * Reflect Library by Parra Studios + * A library for provide reflection and metadata representation. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +typedef struct hello_world_class_type +{ + // These are static attributes that belong to the class + int a; + float b; + char c[10]; + +} * hello_world_class; + +typedef struct hello_world_object_type +{ + // These are attributes that belong to the object + char d; + long e; + +} * hello_world_object; + +int hello_world_object_impl_interface_create(object obj, object_impl impl) +{ + hello_world_object hello_world = (hello_world_object)impl; + + (void)obj; + + EXPECT_NE((void *)NULL, (void *)hello_world); + + // Default values for static attributes (this will be done automatically by the language runtime) + hello_world->d = 'd'; + hello_world->e = 55; + + return 0; +} + +value hello_world_object_impl_interface_get(object obj, object_impl impl, struct accessor_type *accessor) +{ + hello_world_object hello_world = (hello_world_object)impl; + attribute attr = accessor->data.attr; + const char *key = attribute_name(attr); + + (void)obj; + + // Horrible but it is just a ilustrative example + if (strcmp(key, "d") == 0) + { + return value_create_char(hello_world->d); + } + else if (strcmp(key, "e") == 0) + { + return value_create_long(hello_world->e); + } + + return NULL; +} + +int hello_world_object_impl_interface_set(object obj, object_impl impl, struct accessor_type *accessor, value v) +{ + hello_world_object hello_world = (hello_world_object)impl; + attribute attr = accessor->data.attr; + const char *key = attribute_name(attr); + + EXPECT_NE((void *)NULL, (void *)hello_world); + + (void)obj; + + // Horrible but it is just a ilustrative example + if (strcmp(key, "d") == 0) + { + hello_world->d = value_to_char(v); + } + else if (strcmp(key, "e") == 0) + { + hello_world->e = value_to_long(v); + } + + return 0; +} + +value hello_world_object_impl_interface_method_invoke(object obj, object_impl impl, method m, object_args args, size_t size) +{ + // TODO: Maybe we can improve this with other methods and arguments like in reflect_function_test + static const char str[] = "Hello World"; + + (void)obj; + (void)impl; + (void)m; + (void)args; + (void)size; + + return value_create_string(str, sizeof(str) - 1); +} + +value hello_world_object_impl_interface_method_await(object obj, object_impl impl, method m, object_args args, size_t size, object_resolve_callback resolve, object_reject_callback reject, void *ctx) +{ + // TODO + (void)obj; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; + + return NULL; +} + +int hello_world_object_impl_interface_destructor(object obj, object_impl impl) +{ + (void)obj; + (void)impl; + + return 0; +} + +void hello_world_object_impl_interface_destroy(object obj, object_impl impl) +{ + hello_world_object hello_world_obj = static_cast(impl); + + (void)obj; + + delete hello_world_obj; +} + +object_interface hello_world_object_impl_interface_singleton() +{ + static struct object_interface_type hello_world_interface + { + &hello_world_object_impl_interface_create, + &hello_world_object_impl_interface_get, + &hello_world_object_impl_interface_set, + &hello_world_object_impl_interface_method_invoke, + &hello_world_object_impl_interface_method_await, + &hello_world_object_impl_interface_destructor, + &hello_world_object_impl_interface_destroy + }; + + return &hello_world_interface; +} + +int hello_world_class_impl_interface_create(klass cls, class_impl impl) +{ + hello_world_class hello_world = (hello_world_class)impl; + + (void)cls; + + EXPECT_NE((void *)NULL, (void *)hello_world); + + // Default values for static attributes (this will be done automatically by the language runtime) + hello_world->a = 0; + hello_world->b = 0.0f; + hello_world->c[0] = '\0'; + + return 0; +} + +object hello_world_class_impl_interface_constructor(klass cls, class_impl impl, const char *name, constructor ctor, class_args args, size_t size) +{ + hello_world_object hello_world_obj = new hello_world_object_type(); + + (void)impl; + (void)ctor; + + object obj = object_create(name, ACCESSOR_TYPE_STATIC, hello_world_obj, &hello_world_object_impl_interface_singleton, cls); + + if (object_increment_reference(obj) != 0) + { + /* TODO: Abort? */ + } + + if (args == 0) + { + // Default constructor + hello_world_obj->d = 'A'; + hello_world_obj->e = 0L; + } + else if (size == 2) + { + hello_world_obj->d = value_to_char(args[0]); + hello_world_obj->e = value_to_long(args[1]); + } + + return obj; +} + +value hello_world_class_impl_interface_static_get(klass cls, class_impl impl, struct accessor_type *accessor) +{ + hello_world_class hello_world = (hello_world_class)impl; + attribute attr = accessor->data.attr; + char *key = attribute_name(attr); + + (void)cls; + + if (key == NULL) + { + return NULL; + } + + // Horrible but it is just a ilustrative example + if (strcmp(key, "a") == 0) + { + return value_create_int(hello_world->a); + } + else if (strcmp(key, "b") == 0) + { + return value_create_float(hello_world->b); + } + else if (strcmp(key, "c") == 0) + { + return value_create_string(hello_world->c, 9); + } + + return NULL; +} + +int hello_world_class_impl_interface_static_set(klass cls, class_impl impl, struct accessor_type *accessor, value v) +{ + hello_world_class hello_world = (hello_world_class)impl; + attribute attr = accessor->data.attr; + + EXPECT_NE((void *)NULL, (void *)hello_world); + + (void)cls; + + char *key = attribute_name(attr); + + if (key == NULL) + { + return 1; + } + + // Horrible but it is just a ilustrative example + if (strcmp(key, "a") == 0) + { + hello_world->a = value_to_int(v); + } + else if (strcmp(key, "b") == 0) + { + hello_world->b = value_to_float(v); + } + else if (strcmp(key, "c") == 0) + { + strncpy(hello_world->c, value_to_string(v), 9); + } + + return 0; +} + +value hello_world_class_impl_interface_static_invoke(klass cls, class_impl impl, method m, class_args args, size_t argc) +{ + // TODO + (void)cls; + (void)impl; + (void)m; + (void)args; + (void)argc; + + return NULL; +} + +value hello_world_class_impl_interface_static_await(klass cls, class_impl impl, method m, class_args args, size_t size, class_resolve_callback resolve, class_reject_callback reject, void *ctx) +{ + // TODO + (void)cls; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; + + return NULL; +} + +void hello_world_class_impl_interface_destroy(klass cls, class_impl impl) +{ + hello_world_class hellow_world_cls = static_cast(impl); + + (void)cls; + + delete hellow_world_cls; +} + +class_interface hello_world_class_impl_interface_singleton() +{ + static struct class_interface_type hello_world_interface + { + &hello_world_class_impl_interface_create, + &hello_world_class_impl_interface_constructor, + &hello_world_class_impl_interface_static_get, + &hello_world_class_impl_interface_static_set, + &hello_world_class_impl_interface_static_invoke, + &hello_world_class_impl_interface_static_await, + &hello_world_class_impl_interface_destroy + }; + + return &hello_world_interface; +} + +class reflect_object_class_test : public testing::Test +{ +public: +}; + +TEST_F(reflect_object_class_test, DefaultConstructor) +{ + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); + + // Create class + hello_world_class hellow_world_cls = new hello_world_class_type(); + + EXPECT_NE((void *)NULL, (void *)hellow_world_cls); + + klass cls = class_create("HelloWorld", ACCESSOR_TYPE_STATIC, hellow_world_cls, &hello_world_class_impl_interface_singleton); + + EXPECT_EQ((int)class_increment_reference(cls), (int)0); + + // Register constructors + constructor default_ctor = constructor_create(0, VISIBILITY_PUBLIC); + constructor custom_ctor = constructor_create(2, VISIBILITY_PUBLIC); + + ASSERT_NE((constructor)NULL, (constructor)default_ctor); + ASSERT_NE((constructor)NULL, (constructor)custom_ctor); + + type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); + type long_type = type_create(TYPE_LONG, "long", NULL, NULL); + type str_type = type_create(TYPE_STRING, "string", NULL, NULL); + type int_type = type_create(TYPE_INT, "int", NULL, NULL); + type float_type = type_create(TYPE_FLOAT, "float", NULL, NULL); + + ASSERT_NE((type)NULL, (type)char_type); + ASSERT_NE((type)NULL, (type)long_type); + ASSERT_NE((type)NULL, (type)str_type); + ASSERT_NE((type)NULL, (type)int_type); + ASSERT_NE((type)NULL, (type)float_type); + + constructor_set(custom_ctor, 0, "c", char_type); + constructor_set(custom_ctor, 1, "l", long_type); + + EXPECT_EQ((int)0, (int)class_register_constructor(cls, default_ctor)); + EXPECT_EQ((int)0, (int)class_register_constructor(cls, custom_ctor)); + + // Register methods + method test_func_method = method_create(cls, "test_func", 0, NULL, VISIBILITY_PUBLIC, SYNCHRONOUS, NULL); + + ASSERT_NE((method)NULL, (method)test_func_method); + + signature s = method_signature(test_func_method); + + signature_set_return(s, str_type); + + EXPECT_EQ((int)0, (int)class_register_method(cls, test_func_method)); + + // Register static attributes + attribute a_attr = attribute_create(cls, "a", int_type, NULL, VISIBILITY_PUBLIC, NULL); + attribute b_attr = attribute_create(cls, "b", float_type, NULL, VISIBILITY_PUBLIC, NULL); + attribute c_attr = attribute_create(cls, "c", str_type, NULL, VISIBILITY_PUBLIC, NULL); + + ASSERT_NE((attribute)NULL, (attribute)a_attr); + ASSERT_NE((attribute)NULL, (attribute)b_attr); + ASSERT_NE((attribute)NULL, (attribute)c_attr); + + EXPECT_EQ((int)0, (int)class_register_static_attribute(cls, a_attr)); + EXPECT_EQ((int)0, (int)class_register_static_attribute(cls, b_attr)); + EXPECT_EQ((int)0, (int)class_register_static_attribute(cls, c_attr)); + + // Register attributes + attribute d_attr = attribute_create(cls, "d", char_type, NULL, VISIBILITY_PUBLIC, NULL); + attribute e_attr = attribute_create(cls, "e", long_type, NULL, VISIBILITY_PUBLIC, NULL); + + ASSERT_NE((attribute)NULL, (attribute)d_attr); + ASSERT_NE((attribute)NULL, (attribute)e_attr); + + EXPECT_EQ((int)0, (int)class_register_attribute(cls, d_attr)); + EXPECT_EQ((int)0, (int)class_register_attribute(cls, e_attr)); + + // Get and set static attributes from the class + { + value a = class_static_get(cls, "a"); + ASSERT_NE((value)NULL, (value)a); + EXPECT_EQ((int)0, (int)value_to_int(a)); + value_type_destroy(a); + + value b = class_static_get(cls, "b"); + ASSERT_NE((value)NULL, (value)b); + EXPECT_EQ((float)0.0f, (float)value_to_float(b)); + value_type_destroy(b); + + value c = class_static_get(cls, "c"); + ASSERT_NE((value)NULL, (value)c); + EXPECT_EQ((char)'\0', (char)*value_to_string(c)); + value_type_destroy(c); + + value new_a = value_create_int(1234); + ASSERT_EQ((int)0, (int)class_static_set(cls, "a", new_a)); + a = class_static_get(cls, "a"); + EXPECT_NE((value)NULL, (value)a); + EXPECT_EQ((int)1234, (int)value_to_int(a)); + value_type_destroy(a); + value_type_destroy(new_a); + + value new_c = value_create_string("hi", 2); + + ASSERT_EQ((int)0, (int)class_static_set(cls, "c", new_c)); + c = class_static_get(cls, "c"); + EXPECT_NE((value)NULL, (value)c); + EXPECT_EQ((char)'h', (char)value_to_string(c)[0]); + EXPECT_EQ((char)'i', (char)value_to_string(c)[1]); + value_type_destroy(c); + value_type_destroy(new_c); + } + + // Create object (default constructor) + { + value args[] = { + NULL + }; + + type_id ctor_ids[] = { + TYPE_INVALID + }; + + constructor ctor = class_constructor(cls, ctor_ids, 0); + + ASSERT_EQ((constructor)ctor, (constructor)default_ctor); + + object obj = class_new(cls, "helloWorldObj", ctor, args, 0); + ASSERT_NE((object)NULL, (object)obj); + + // Get & set attributes from object + { + value d = object_get(obj, "d"); + ASSERT_NE((value)NULL, (value)d); + EXPECT_EQ((char)'d', (char)value_to_char(d)); + value_type_destroy(d); + + value e = object_get(obj, "e"); + ASSERT_NE((value)NULL, (value)e); + EXPECT_EQ((long)55L, (long)value_to_long(e)); + value_type_destroy(e); + + value new_d = value_create_char('M'); + ASSERT_EQ((char)0, (char)object_set(obj, "d", new_d)); + d = object_get(obj, "d"); + EXPECT_NE((value)NULL, (value)d); + EXPECT_EQ((char)'M', (char)value_to_char(d)); + value_type_destroy(d); + value_type_destroy(new_d); + + value new_e = value_create_long(1234); + ASSERT_EQ((long)0, (long)object_set(obj, "e", new_e)); + e = object_get(obj, "e"); + EXPECT_NE((value)NULL, (value)e); + EXPECT_EQ((long)1234L, (long)value_to_long(e)); + value_type_destroy(e); + value_type_destroy(new_e); + } + + // Test object call + type_id method_ids[] = { + TYPE_INVALID + }; + + method m = class_method(cls, "test_func", TYPE_STRING, method_ids, 0); + + ASSERT_EQ((method)m, (method)test_func_method); + + value ret = object_call(obj, m, args, 0); + + ASSERT_NE((value)NULL, (value)ret); + + ASSERT_STREQ(value_to_string(ret), "Hello World"); + + value_type_destroy(ret); + + // TODO: Test object await + + object_destroy(obj); + } + + // Create object (custom constructor) + { + value args[] = { + value_create_char('F'), + value_create_long(3435L) + }; + + type_id ctor_ids[] = { + TYPE_CHAR, + TYPE_LONG + }; + + constructor ctor = class_constructor(cls, ctor_ids, 2); + + ASSERT_EQ((constructor)ctor, (constructor)custom_ctor); + + object obj = class_new(cls, "helloWorldObj", ctor, args, 2); + ASSERT_NE((object)NULL, (object)obj); + + value_type_destroy(args[0]); + value_type_destroy(args[1]); + + // Get attributes from object + { + value d = object_get(obj, "d"); + ASSERT_NE((value)NULL, (value)d); + EXPECT_EQ((char)'F', (char)value_to_char(d)); + value_type_destroy(d); + + value e = object_get(obj, "e"); + ASSERT_NE((value)NULL, (value)e); + EXPECT_EQ((long)3435L, (long)value_to_long(e)); + value_type_destroy(e); + } + + // Test object call + type_id method_ids[] = { + TYPE_INVALID + }; + + method m = class_method(cls, "test_func", TYPE_STRING, method_ids, 0); + + ASSERT_EQ((method)m, (method)test_func_method); + + value ret = object_call(obj, m, args, 0); + + ASSERT_NE((value)NULL, (value)ret); + + ASSERT_STREQ(value_to_string(ret), "Hello World"); + + value_type_destroy(ret); + + // TODO: Test object await + + object_destroy(obj); + } + + type_destroy(char_type); + type_destroy(long_type); + type_destroy(str_type); + type_destroy(int_type); + type_destroy(float_type); + + class_destroy(cls); +} diff --git a/source/tests/reflect_scope_test/CMakeLists.txt b/source/tests/reflect_scope_test/CMakeLists.txt index 773e79a8e3..83990f2359 100644 --- a/source/tests/reflect_scope_test/CMakeLists.txt +++ b/source/tests/reflect_scope_test/CMakeLists.txt @@ -68,6 +68,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,16 +94,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -110,11 +115,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_scope_test/source/main.cpp b/source/tests/reflect_scope_test/source/main.cpp index 6123b85dbf..cba58797ca 100644 --- a/source/tests/reflect_scope_test/source/main.cpp +++ b/source/tests/reflect_scope_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_scope_test/source/reflect_scope_test.cpp b/source/tests/reflect_scope_test/source/reflect_scope_test.cpp index 5b4557d46d..2e58c50b04 100644 --- a/source/tests/reflect_scope_test/source/reflect_scope_test.cpp +++ b/source/tests/reflect_scope_test/source/reflect_scope_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,11 @@ * */ -#include +#include #include #include +#include #include @@ -44,9 +45,9 @@ typedef struct function_impl_example_type } * function_impl_example; -void function_example(char c, int i, void * p) +void function_example(char c, int i, void *p) { - struct example_arg_type * e = (struct example_arg_type *)p; + struct example_arg_type *e = (struct example_arg_type *)p; log_write("metacall", LOG_LEVEL_DEBUG, "char: %c; int: %d; ptr: %p", c, i, p); @@ -69,17 +70,33 @@ int function_example_interface_create(function func, function_impl impl) return 1; } -function_return function_example_interface_invoke(function func, function_impl func_impl, function_args args) +function_return function_example_interface_invoke(function func, function_impl func_impl, function_args args, size_t size) { function_impl_example example_impl = (function_impl_example)func_impl; (void)func; + (void)size; example_impl->ptr(*((char *)(args[0])), *((int *)(args[1])), (void *)(args[2])); return NULL; } +function_return function_example_interface_await(function func, function_impl impl, function_args args, size_t size, function_resolve_callback resolve_callback, function_reject_callback reject_callback, void *context) +{ + /* TODO */ + + (void)func; + (void)impl; + (void)args; + (void)size; + (void)resolve_callback; + (void)reject_callback; + (void)context; + + return NULL; +} + void function_example_interface_destroy(function func, function_impl func_impl) { (void)func; @@ -89,10 +106,10 @@ void function_example_interface_destroy(function func, function_impl func_impl) function_interface function_example_singleton() { - static struct function_interface_type example_interface = - { + static struct function_interface_type example_interface = { &function_example_interface_create, &function_example_interface_invoke, + &function_example_interface_await, &function_example_interface_destroy }; @@ -101,34 +118,34 @@ function_interface function_example_singleton() class reflect_scope_test : public testing::Test { - public: +public: }; TEST_F(reflect_scope_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); // Initialize serial - EXPECT_EQ((int) 0, (int) serial_initialize()); + EXPECT_EQ((int)0, (int)serial_initialize()); // Create serial serial s = serial_create("rapid_json"); scope sp = scope_create("test"); - EXPECT_NE((scope) sp, (scope) NULL); + EXPECT_NE((scope)sp, (scope)NULL); type char_type = type_create(TYPE_CHAR, "char", NULL, NULL); type int_type = type_create(TYPE_INT, "int", NULL, NULL); type ptr_type = type_create(TYPE_PTR, "ptr", NULL, NULL); - EXPECT_NE((type) char_type, (type) NULL); - EXPECT_NE((type) int_type, (type) NULL); - EXPECT_NE((type) ptr_type, (type) NULL); + EXPECT_NE((type)char_type, (type)NULL); + EXPECT_NE((type)int_type, (type)NULL); + EXPECT_NE((type)ptr_type, (type)NULL); if (sp && char_type != NULL && int_type != NULL && ptr_type != NULL) { @@ -138,7 +155,7 @@ TEST_F(reflect_scope_test, DefaultConstructor) f1 = function_create("example", 3, example_impl, &function_example_singleton); - EXPECT_NE((function) f1, (function) NULL); + EXPECT_NE((function)f1, (function)NULL); if (f1 != NULL) { @@ -146,14 +163,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f1), 1, "i", int_type); signature_set(function_signature(f1), 2, "p", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f1), f1), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f1), value_create_function(f1)), (int)0); } function_impl_example example_asd_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f2 = function_create("example_asd", 3, example_asd_impl, &function_example_singleton); - EXPECT_NE((function) f2, (function) NULL); + EXPECT_NE((function)f2, (function)NULL); if (f2 != NULL) { @@ -161,14 +178,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f2), 1, "o", int_type); signature_set(function_signature(f2), 2, "u", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f2), f2), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f2), value_create_function(f2)), (int)0); } function_impl_example example_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f3 = function_create("example_ret", 3, example_ret_impl, &function_example_singleton); - EXPECT_NE((function) f3, (function) NULL); + EXPECT_NE((function)f3, (function)NULL); if (f3 != NULL) { @@ -177,14 +194,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f3), 1, "o", int_type); signature_set(function_signature(f3), 2, "u", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f3), f3), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f3), value_create_function(f3)), (int)0); } function_impl_example example_duck_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f4 = function_create("example_duck", 3, example_duck_impl, &function_example_singleton); - EXPECT_NE((function) f4, (function) NULL); + EXPECT_NE((function)f4, (function)NULL); if (f4 != NULL) { @@ -192,14 +209,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f4), 1, "o", NULL); signature_set(function_signature(f4), 2, "u", NULL); - EXPECT_EQ((int) scope_define(sp, function_name(f4), f4), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f4), value_create_function(f4)), (int)0); } function_impl_example example_duck_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f5 = function_create("example_duck_ret", 3, example_duck_ret_impl, &function_example_singleton); - EXPECT_NE((function) f5, (function) NULL); + EXPECT_NE((function)f5, (function)NULL); if (f5 != NULL) { @@ -208,14 +225,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f5), 1, "do", NULL); signature_set(function_signature(f5), 2, "dafu", NULL); - EXPECT_EQ((int) scope_define(sp, function_name(f5), f5), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f5), value_create_function(f5)), (int)0); } function_impl_example example_duck_mix_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f6 = function_create("example_duck_mix", 4, example_duck_mix_impl, &function_example_singleton); - EXPECT_NE((function) f6, (function) NULL); + EXPECT_NE((function)f6, (function)NULL); if (f6 != NULL) { @@ -224,14 +241,14 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f6), 2, "dafu", NULL); signature_set(function_signature(f6), 3, "dafu", ptr_type); - EXPECT_EQ((int) scope_define(sp, function_name(f6), f6), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f6), value_create_function(f6)), (int)0); } function_impl_example example_duck_mix_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f7 = function_create("example_duck_mix_ret", 4, example_duck_mix_ret_impl, &function_example_singleton); - EXPECT_NE((function) f7, (function) NULL); + EXPECT_NE((function)f7, (function)NULL); if (f7 != NULL) { @@ -241,31 +258,31 @@ TEST_F(reflect_scope_test, DefaultConstructor) signature_set(function_signature(f7), 2, "dafu", ptr_type); signature_set(function_signature(f7), 3, "dafufs", NULL); - EXPECT_EQ((int) scope_define(sp, function_name(f7), f7), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f7), value_create_function(f7)), (int)0); } function_impl_example example_empty_ret_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f8 = function_create("example_empty_ret", 0, example_empty_ret_impl, &function_example_singleton); - EXPECT_NE((function) f8, (function) NULL); + EXPECT_NE((function)f8, (function)NULL); if (f8 != NULL) { signature_set_return(function_signature(f8), int_type); - EXPECT_EQ((int) scope_define(sp, function_name(f8), f8), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f8), value_create_function(f8)), (int)0); } function_impl_example example_empty_impl = (function_impl_example)malloc(sizeof(struct function_impl_example_type)); f9 = function_create("example_empty", 0, example_empty_impl, &function_example_singleton); - EXPECT_NE((function) f9, (function) NULL); + EXPECT_NE((function)f9, (function)NULL); if (f9 != NULL) { - EXPECT_EQ((int) scope_define(sp, function_name(f9), f9), (int) 0); + EXPECT_EQ((int)scope_define(sp, function_name(f9), value_create_function(f9)), (int)0); } { @@ -273,11 +290,11 @@ TEST_F(reflect_scope_test, DefaultConstructor) value v = scope_metadata(sp); - EXPECT_NE((value) NULL, (value) v); + EXPECT_NE((value)NULL, (value)v); memory_allocator allocator = memory_allocator_std(&std::malloc, &std::realloc, &std::free); - char * str = serial_serialize(s, v, &size, allocator); + char *str = serial_serialize(s, v, &size, allocator); log_write("metacall", LOG_LEVEL_DEBUG, "Scope serialization info: %s", str); @@ -296,7 +313,7 @@ TEST_F(reflect_scope_test, DefaultConstructor) } // Clear serial - EXPECT_EQ((int) 0, (int) serial_clear(s)); + EXPECT_EQ((int)0, (int)serial_clear(s)); // Destroy serial serial_destroy(); diff --git a/source/tests/reflect_value_cast_test/CMakeLists.txt b/source/tests/reflect_value_cast_test/CMakeLists.txt index 8ed2743b12..4b28d3a1a9 100644 --- a/source/tests/reflect_value_cast_test/CMakeLists.txt +++ b/source/tests/reflect_value_cast_test/CMakeLists.txt @@ -74,6 +74,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -86,16 +100,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -116,11 +121,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) diff --git a/source/tests/reflect_value_cast_test/source/main.cpp b/source/tests/reflect_value_cast_test/source/main.cpp index 6123b85dbf..cba58797ca 100644 --- a/source/tests/reflect_value_cast_test/source/main.cpp +++ b/source/tests/reflect_value_cast_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp index 4c1d5962a3..988e7a607a 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_bool_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_bool_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_bool_test, bool_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) b, (char) value_to_char(v)); + EXPECT_EQ((char)b, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) b, (short) value_to_short(v)); + EXPECT_EQ((short)b, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) b, (int) value_to_int(v)); + EXPECT_EQ((int)b, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) b, (long) value_to_long(v)); + EXPECT_EQ((long)b, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) b, (float) value_to_float(v)); + EXPECT_EQ((float)b, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_bool_test, bool_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) b, (double) value_to_double(v)); + EXPECT_EQ((double)b, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp index bb12292da6..0bbe2bbecf 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_char_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_char_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_char_test, char_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_char_test, char_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_char_test, char_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) c, (char) value_to_char(v)); + EXPECT_EQ((char)c, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_char_test, char_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) c, (short) value_to_short(v)); + EXPECT_EQ((short)c, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_char_test, char_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) c, (int) value_to_int(v)); + EXPECT_EQ((int)c, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_char_test, char_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) c, (long) value_to_long(v)); + EXPECT_EQ((long)c, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_char_test, char_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) c, (float) value_to_float(v)); + EXPECT_EQ((float)c, (float)value_to_float(v)); value_destroy(v); } @@ -114,8 +114,7 @@ TEST_F(reflect_value_cast_char_test, char_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) c, (double) value_to_double(v)); + EXPECT_EQ((double)c, (double)value_to_double(v)); value_destroy(v); } - diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp index e5bedb041b..8991352731 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_double_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_double_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_double_test, double_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_double_test, double_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_double_test, double_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) d, (char) value_to_char(v)); + EXPECT_EQ((char)d, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_double_test, double_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) d, (short) value_to_short(v)); + EXPECT_EQ((short)d, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_double_test, double_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) d, (int) value_to_int(v)); + EXPECT_EQ((int)d, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_double_test, double_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) d, (long) value_to_long(v)); + EXPECT_EQ((long)d, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_double_test, double_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) d, (float) value_to_float(v)); + EXPECT_EQ((float)d, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_double_test, double_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) d, (double) value_to_double(v)); + EXPECT_EQ((double)d, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp index c83e1719f8..f1ffedc062 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_float_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_float_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_float_test, float_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_float_test, float_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_float_test, float_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) f, (char) value_to_char(v)); + EXPECT_EQ((char)f, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_float_test, float_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) f, (short) value_to_short(v)); + EXPECT_EQ((short)f, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_float_test, float_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) f, (int) value_to_int(v)); + EXPECT_EQ((int)f, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_float_test, float_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) f, (long) value_to_long(v)); + EXPECT_EQ((long)f, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_float_test, float_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) f, (float) value_to_float(v)); + EXPECT_EQ((float)f, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_float_test, float_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) f, (double) value_to_double(v)); + EXPECT_EQ((double)f, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp index d6c1633555..7e6e2aa35d 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_int_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_int_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_int_test, int_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_int_test, int_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_int_test, int_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) i, (char) value_to_char(v)); + EXPECT_EQ((char)i, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_int_test, int_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) i, (short) value_to_short(v)); + EXPECT_EQ((short)i, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_int_test, int_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) i, (int) value_to_int(v)); + EXPECT_EQ((int)i, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_int_test, int_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) i, (long) value_to_long(v)); + EXPECT_EQ((long)i, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_int_test, int_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) i, (float) value_to_float(v)); + EXPECT_EQ((float)i, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_int_test, int_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) i, (double) value_to_double(v)); + EXPECT_EQ((double)i, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp index db5c9b9114..9258a11e32 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_long_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_long_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_long_test, long_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_long_test, long_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_long_test, long_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) l, (char) value_to_char(v)); + EXPECT_EQ((char)l, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_long_test, long_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) l, (short) value_to_short(v)); + EXPECT_EQ((short)l, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_long_test, long_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) l, (int) value_to_int(v)); + EXPECT_EQ((int)l, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_long_test, long_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) l, (long) value_to_long(v)); + EXPECT_EQ((long)l, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_long_test, long_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) l, (float) value_to_float(v)); + EXPECT_EQ((float)l, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_long_test, long_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) l, (double) value_to_double(v)); + EXPECT_EQ((double)l, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp b/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp index 02227e9be8..e33d576373 100644 --- a/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp +++ b/source/tests/reflect_value_cast_test/source/reflect_value_cast_short_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ * */ -#include +#include #include #include class reflect_value_cast_short_test : public testing::Test { - public: +public: }; TEST_F(reflect_value_cast_short_test, short_to_bool_cast) @@ -36,7 +36,7 @@ TEST_F(reflect_value_cast_short_test, short_to_bool_cast) v = value_type_cast(v, TYPE_BOOL); - EXPECT_EQ((boolean) b, (boolean) value_to_bool(v)); + EXPECT_EQ((boolean)b, (boolean)value_to_bool(v)); value_destroy(v); } @@ -49,7 +49,7 @@ TEST_F(reflect_value_cast_short_test, short_to_char_cast) v = value_type_cast(v, TYPE_CHAR); - EXPECT_EQ((char) s, (char) value_to_char(v)); + EXPECT_EQ((char)s, (char)value_to_char(v)); value_destroy(v); } @@ -62,7 +62,7 @@ TEST_F(reflect_value_cast_short_test, short_to_short_cast) v = value_type_cast(v, TYPE_SHORT); - EXPECT_EQ((short) s, (short) value_to_short(v)); + EXPECT_EQ((short)s, (short)value_to_short(v)); value_destroy(v); } @@ -75,7 +75,7 @@ TEST_F(reflect_value_cast_short_test, short_to_int_cast) v = value_type_cast(v, TYPE_INT); - EXPECT_EQ((int) s, (int) value_to_int(v)); + EXPECT_EQ((int)s, (int)value_to_int(v)); value_destroy(v); } @@ -88,7 +88,7 @@ TEST_F(reflect_value_cast_short_test, short_to_long_cast) v = value_type_cast(v, TYPE_LONG); - EXPECT_EQ((long) s, (long) value_to_long(v)); + EXPECT_EQ((long)s, (long)value_to_long(v)); value_destroy(v); } @@ -101,7 +101,7 @@ TEST_F(reflect_value_cast_short_test, short_to_float_cast) v = value_type_cast(v, TYPE_FLOAT); - EXPECT_EQ((float) s, (float) value_to_float(v)); + EXPECT_EQ((float)s, (float)value_to_float(v)); value_destroy(v); } @@ -114,7 +114,7 @@ TEST_F(reflect_value_cast_short_test, short_to_double_cast) v = value_type_cast(v, TYPE_DOUBLE); - EXPECT_EQ((double) s, (double) value_to_double(v)); + EXPECT_EQ((double)s, (double)value_to_double(v)); value_destroy(v); } diff --git a/source/tests/sanitizer/lsan.supp b/source/tests/sanitizer/lsan.supp new file mode 100644 index 0000000000..8550a51160 --- /dev/null +++ b/source/tests/sanitizer/lsan.supp @@ -0,0 +1,42 @@ +# List of supressions for leak sanitizer +# +# IMPORTANT: Do NOT add blank lines between the script, +# it seems that gcc sanitizer does not support that. +# +# At this point there are leaks from the runtimes but +# I am not sure all of them are due to metacall (probably +# some of them are due to GC errors but not all of them). +# We disable them until we debug those cases (if any) in depth. +# +# +# Python +# +leak:_PyObject_Malloc +leak:_PyObject_Realloc +leak:_PyObject_GC_Resize +leak:_PyMem_RawRealloc +leak:_PyMem_RawMalloc +leak:PyThread_allocate_lock +leak:libpython* +# +# NodeJS +# +# Suppress small (intentional) leaks in glibc +leak:libc.so +# Supress node library (for now) +leak:libnode* +# +# Ruby +# +leak:libruby* +# +# DotNet Core +# +leak:libcoreclr* +leak:libicuuc* +# TODO: Implement assembly unloading with loader context +leak:System.Private.CoreLib.dll +# +# Rust +# +leak:libLLVM* \ No newline at end of file diff --git a/source/tests/sanitizer/tsan.supp b/source/tests/sanitizer/tsan.supp new file mode 100644 index 0000000000..a653d097e7 --- /dev/null +++ b/source/tests/sanitizer/tsan.supp @@ -0,0 +1,76 @@ +# List of supressions for thread sanitizer +# +# IMPORTANT: Do NOT add blank lines between the script, +# it seems that gcc sanitizer does not support that. +# +# At this point there are data races from the runtimes but +# I am not sure all of them are due to metacall (probably +# some of them are due to race conditions but not all of them). +# We disable them until we debug those cases (if any) in depth. +# +# +# Python +# +#called_from_lib:libpython* +# +# Suppress race condition from Python 10 async io: https://github.com/python/cpython/issues/116912 +race:socketpair +race:socket_socketpair +race:sock_close +race:sock_send_impl +# +# NodeJS +# +race:v8::platform::tracing::TracingController::GetCategoryGroupEnabled +race:v8::platform::DefaultJobWorker::~DefaultJobWorker +race:v8::platform::DefaultJobState::~DefaultJobState +race:v8::internal::ScavengerCollector::JobTask::~JobTask +race:heap::base::Worklist, (unsigned short)256>::Local::Pop(std::pair*) +# +# After version 108, NodeJS has started to fail without stack trace, for example: +# +# WARNING: ThreadSanitizer: data race (pid=5179) +# Write of size 8 at 0x723000007538 by thread T4: +# #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.2+0x9b42b) (BuildId: f23ac1bd2939198f3fef776fd2a1312e536dcf1b) +# #1 (libnode.so.109+0xdb8fa9) (BuildId: fa61d14d9def07b0f94f901c16a182f9e3a944ae) +# +# Now if we use addr2line: +# +# addr2line -f -e /lib/x86_64-linux-gnu/libnode.so.109 0xdb8fa9 +# _ZN2v88platform16DefaultJobWorkerD0Ev +# ??:? +# +# The symbols are exactly the same of the well known suppressions already listed before +# I have tried compiling NodeJS with thread sanitizer support and also using llvm-symbolizer +# but I had no luck making those symbols work in the last stack traces, so for now.. I am going +# to suppress all data races from NodeJS in order to avoid false positives. +# +race:libnode* +# +# Ruby +# +#called_from_lib:libruby* +# +# DotNet Core +# +called_from_lib:libcoreclr* +called_from_lib:libclrjit* +# +# Java +# +called_from_lib:libjvm* +# +# WASM (TODO: This must be reviewed, probably it's a false positive) +# +called_from_lib:libwasmtime* +# +# C +# +mutex:clang_install_aborting_llvm_fatal_error_handler +mutex:libclang-* +# +# Rust +# +race:libLLVM* +race:librustc_driver-* +race:libstd-* \ No newline at end of file diff --git a/source/tests/serial_test/CMakeLists.txt b/source/tests/serial_test/CMakeLists.txt index 6f2f06feb2..12356bc112 100644 --- a/source/tests/serial_test/CMakeLists.txt +++ b/source/tests/serial_test/CMakeLists.txt @@ -1,3 +1,8 @@ +# Check if this serial is enabled +if(NOT OPTION_BUILD_SERIALS OR NOT OPTION_BUILD_SERIALS_METACALL OR NOT OPTION_BUILD_SERIALS_RAPID_JSON) + return() +endif() + # # Executable name and options # @@ -68,6 +73,20 @@ target_include_directories(${target} PRIVATE ${DEFAULT_INCLUDE_DIRECTORIES} ${PROJECT_BINARY_DIR}/source/include + + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ ) # @@ -80,16 +99,7 @@ target_link_libraries(${target} GTest - ${META_PROJECT_NAME}::version - ${META_PROJECT_NAME}::preprocessor - ${META_PROJECT_NAME}::environment - ${META_PROJECT_NAME}::format - ${META_PROJECT_NAME}::log - ${META_PROJECT_NAME}::memory - ${META_PROJECT_NAME}::adt - ${META_PROJECT_NAME}::reflect - ${META_PROJECT_NAME}::dynlink - ${META_PROJECT_NAME}::serial + ${META_PROJECT_NAME}::metacall ) # @@ -110,11 +120,20 @@ target_compile_options(${target} ${DEFAULT_COMPILE_OPTIONS} ) +# +# Compile features +# + +target_compile_features(${target} + PRIVATE + cxx_std_17 +) + # # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE ${DEFAULT_LINKER_OPTIONS} ) @@ -127,6 +146,14 @@ add_test(NAME ${target} COMMAND $ ) +# +# Define dependencies +# + +add_dependencies(${target} + metacall_serial + rapid_json_serial +) # # Define test labels # diff --git a/source/tests/serial_test/source/main.cpp b/source/tests/serial_test/source/main.cpp index 6123b85dbf..cba58797ca 100644 --- a/source/tests/serial_test/source/main.cpp +++ b/source/tests/serial_test/source/main.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ * */ -#include +#include -int main(int argc, char * argv[]) +int main(int argc, char *argv[]) { - ::testing::InitGoogleMock(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/tests/serial_test/source/serial_test.cpp b/source/tests/serial_test/source/serial_test.cpp index 165bb07a1c..bf2d158acb 100644 --- a/source/tests/serial_test/source/serial_test.cpp +++ b/source/tests/serial_test/source/serial_test.cpp @@ -2,7 +2,7 @@ * Reflect Library by Parra Studios * A library for provide reflection and metadata representation. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ * */ -#include +#include + +#include #include @@ -26,31 +28,31 @@ class serial_test : public testing::Test { - public: - void create_serial(const char * name, const char * extension) +public: + void create_serial(const char *name, const char *extension) { serial s = serial_create(name); - ASSERT_NE((serial) NULL, (serial) s); + ASSERT_NE((serial)NULL, (serial)s); - EXPECT_EQ((int) 0, (int) strcmp(name, serial_name(s))); + EXPECT_STREQ(name, serial_name(s)); - EXPECT_EQ((int) 0, (int) strcmp(extension, serial_extension(s))); + EXPECT_STREQ(extension, serial_extension(s)); } - const char * rapid_json_name() + const char *rapid_json_name() { return "rapid_json"; } - const char * rapid_json_extension() + const char *rapid_json_extension() { return "json"; } - const char * metacall_name() + const char *metacall_name() { return "metacall"; } - const char * metacall_extension() + const char *metacall_extension() { return "meta"; } @@ -58,19 +60,19 @@ class serial_test : public testing::Test TEST_F(serial_test, DefaultConstructor) { - EXPECT_EQ((int) 0, (int) log_configure("metacall", - log_policy_format_text(), - log_policy_schedule_sync(), - log_policy_storage_sequential(), - log_policy_stream_stdio(stdout))); + EXPECT_EQ((int)0, (int)log_configure("metacall", + log_policy_format_text(), + log_policy_schedule_sync(), + log_policy_storage_sequential(), + log_policy_stream_stdio(stdout))); // Create allocator memory_allocator allocator = memory_allocator_std(&malloc, &realloc, &free); - EXPECT_NE((memory_allocator) NULL, (memory_allocator) allocator); + EXPECT_NE((memory_allocator)NULL, (memory_allocator)allocator); // Initialize serial - EXPECT_EQ((int) 0, (int) serial_initialize()); + EXPECT_EQ((int)0, (int)serial_initialize()); // Create RapidJSON serial create_serial(rapid_json_name(), rapid_json_extension()); @@ -82,8 +84,7 @@ TEST_F(serial_test, DefaultConstructor) { static const char hello_world[] = "hello world"; - static const value value_list[] = - { + static const value value_list[] = { value_create_int(244), value_create_double(6.8), value_create_string(hello_world, sizeof(hello_world) - 1) @@ -92,22 +93,19 @@ TEST_F(serial_test, DefaultConstructor) static const char map_index_a[] = "aaa"; static const char map_index_b[] = "bbb"; - static const value value_map_a[] = - { + static const value value_map_a[] = { value_create_string(map_index_a, sizeof(map_index_a) - 1), value_create_double(3.333) }; - static const value value_map_b[] = - { + static const value value_map_b[] = { value_create_string(map_index_b, sizeof(map_index_b) - 1), value_create_double(4.5) }; - static const value value_map[] = - { - value_create_array(value_map_a, sizeof(value_map_a)), - value_create_array(value_map_b, sizeof(value_map_b)) + static const value value_map[] = { + value_create_array(value_map_a, sizeof(value_map_a) / sizeof(value_map_a[0])), + value_create_array(value_map_b, sizeof(value_map_b) / sizeof(value_map_b[0])) }; static const size_t value_list_size = sizeof(value_list) / sizeof(value_list[0]); @@ -126,6 +124,8 @@ TEST_F(serial_test, DefaultConstructor) static const char json_string[] = "\"Hello World\""; static const char json_string_value[] = "Hello World"; + static const char json_empty_array[] = "[]"; + size_t serialize_size = 0; serial s = serial_create(rapid_json_name()); @@ -133,14 +133,14 @@ TEST_F(serial_test, DefaultConstructor) // Create value array from value list value v = value_create_array(value_list, value_list_size); - EXPECT_NE((value) NULL, (value) v); + EXPECT_NE((value)NULL, (value)v); // Serialize value array into buffer - char * buffer = serial_serialize(s, v, &serialize_size, allocator); + char *buffer = serial_serialize(s, v, &serialize_size, allocator); - EXPECT_EQ((size_t) sizeof(value_list_str), (size_t) serialize_size); - EXPECT_NE((char *) NULL, (char *) buffer); - EXPECT_EQ((int) 0, (int) strcmp(buffer, value_list_str)); + EXPECT_EQ((size_t)sizeof(value_list_str), (size_t)serialize_size); + EXPECT_NE((char *)NULL, (char *)buffer); + EXPECT_STREQ(buffer, value_list_str); value_destroy(v); @@ -154,20 +154,20 @@ TEST_F(serial_test, DefaultConstructor) // Create value map from value list map v = value_create_map(value_map, value_map_size); - EXPECT_NE((value) NULL, (value) v); + EXPECT_NE((value)NULL, (value)v); // Serialize value map into buffer buffer = serial_serialize(s, v, &serialize_size, allocator); - EXPECT_EQ((size_t) sizeof(value_map_str), (size_t) serialize_size); - EXPECT_NE((value) NULL, (value) v); - EXPECT_EQ((int) 0, (int) strcmp(buffer, value_map_str)); + EXPECT_EQ((size_t)sizeof(value_map_str), (size_t)serialize_size); + EXPECT_NE((value)NULL, (value)v); + EXPECT_STREQ(buffer, value_map_str); - value * v_map = value_to_map(v); + value *v_map = value_to_map(v); for (size_t iterator = 0; iterator < value_map_size; ++iterator) { - value * tupla = value_to_array(v_map[iterator]); + value *tupla = value_to_array(v_map[iterator]); value_destroy(tupla[0]); value_destroy(tupla[1]); @@ -178,25 +178,41 @@ TEST_F(serial_test, DefaultConstructor) value_destroy(v); memory_allocator_deallocate(allocator, buffer); - + + // Create an empty array + v = value_create_array(NULL, 0); + + EXPECT_NE((value)NULL, (value)v); + + // Serialize empty array into buffer + buffer = serial_serialize(s, v, &serialize_size, allocator); + + EXPECT_EQ((size_t)sizeof(json_empty_array), (size_t)serialize_size); + EXPECT_NE((value)NULL, (value)v); + EXPECT_STREQ(buffer, json_empty_array); + + value_destroy(v); + + memory_allocator_deallocate(allocator, buffer); + // Deserialize json buffer array into value v = serial_deserialize(s, json_buffer_array, sizeof(json_buffer_array), allocator); - EXPECT_EQ((type_id) TYPE_ARRAY, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) json_buffer_array_size, (size_t) value_type_count(v)); + EXPECT_EQ((type_id)TYPE_ARRAY, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)json_buffer_array_size, (size_t)value_type_count(v)); - value * v_array = value_to_array(v); + value *v_array = value_to_array(v); - EXPECT_NE((value *) NULL, (value *) v_array); + EXPECT_NE((value *)NULL, (value *)v_array); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(v_array[0])); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(v_array[0]), "asdf")); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(v_array[0])); + EXPECT_STREQ(value_to_string(v_array[0]), "asdf"); - EXPECT_EQ((type_id) TYPE_INT, (type_id) value_type_id(v_array[1])); - EXPECT_EQ((int) 443, (int) value_to_int(v_array[1])); + EXPECT_EQ((type_id)TYPE_INT, (type_id)value_type_id(v_array[1])); + EXPECT_EQ((int)443, (int)value_to_int(v_array[1])); - EXPECT_EQ((type_id) TYPE_FLOAT, (type_id) value_type_id(v_array[2])); - EXPECT_EQ((float) 3.2f, (float) value_to_float(v_array[2])); + EXPECT_EQ((type_id)TYPE_FLOAT, (type_id)value_type_id(v_array[2])); + EXPECT_EQ((float)3.2f, (float)value_to_float(v_array[2])); for (size_t iterator = 0; iterator < value_list_size; ++iterator) { @@ -208,23 +224,23 @@ TEST_F(serial_test, DefaultConstructor) // Deserialize json buffer map into value v = serial_deserialize(s, json_buffer_map, sizeof(json_buffer_map), allocator); - EXPECT_EQ((type_id) TYPE_MAP, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) json_buffer_map_size, (size_t) value_type_count(v)); + EXPECT_EQ((type_id)TYPE_MAP, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)json_buffer_map_size, (size_t)value_type_count(v)); v_map = value_to_map(v); - EXPECT_NE((value *) NULL, (value *) v_map); + EXPECT_NE((value *)NULL, (value *)v_map); - EXPECT_EQ((type_id) TYPE_ARRAY, (type_id) value_type_id(v_map[0])); - EXPECT_EQ((type_id) TYPE_ARRAY, (type_id) value_type_id(v_map[1])); + EXPECT_EQ((type_id)TYPE_ARRAY, (type_id)value_type_id(v_map[0])); + EXPECT_EQ((type_id)TYPE_ARRAY, (type_id)value_type_id(v_map[1])); - value * tupla = value_to_array(v_map[0]); + value *tupla = value_to_array(v_map[0]); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(tupla[0])); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(tupla[0]), "abc")); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(tupla[0])); + EXPECT_STREQ(value_to_string(tupla[0]), "abc"); - EXPECT_EQ((type_id) TYPE_FLOAT, (type_id) value_type_id(tupla[1])); - EXPECT_EQ((float) 9.9f, (float) value_to_float(tupla[1])); + EXPECT_EQ((type_id)TYPE_FLOAT, (type_id)value_type_id(tupla[1])); + EXPECT_EQ((float)9.9f, (float)value_to_float(tupla[1])); value_destroy(tupla[0]); value_destroy(tupla[1]); @@ -233,11 +249,11 @@ TEST_F(serial_test, DefaultConstructor) tupla = value_to_array(v_map[1]); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(tupla[0])); - EXPECT_EQ((int) 0, (int) strcmp(value_to_string(tupla[0]), "cde")); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(tupla[0])); + EXPECT_STREQ(value_to_string(tupla[0]), "cde"); - EXPECT_EQ((type_id) TYPE_FLOAT, (type_id) value_type_id(tupla[1])); - EXPECT_EQ((float) 1.5f, (float) value_to_float(tupla[1])); + EXPECT_EQ((type_id)TYPE_FLOAT, (type_id)value_type_id(tupla[1])); + EXPECT_EQ((float)1.5f, (float)value_to_float(tupla[1])); value_destroy(tupla[0]); value_destroy(tupla[1]); @@ -249,27 +265,27 @@ TEST_F(serial_test, DefaultConstructor) // Deserialize json boolean primitive type into value v = serial_deserialize(s, json_true, sizeof(json_true), allocator); - EXPECT_EQ((type_id) TYPE_BOOL, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) sizeof(boolean), (size_t)value_type_size(v)); - EXPECT_EQ((boolean) 1L, (boolean) value_to_bool(v)); + EXPECT_EQ((type_id)TYPE_BOOL, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)sizeof(boolean), (size_t)value_type_size(v)); + EXPECT_EQ((boolean)1L, (boolean)value_to_bool(v)); value_destroy(v); // Deserialize json number primitive type into value v = serial_deserialize(s, json_number, sizeof(json_number), allocator); - EXPECT_EQ((type_id) TYPE_INT, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) sizeof(int), (size_t) value_type_size(v)); - EXPECT_EQ((int) 23434, (int) value_to_int(v)); + EXPECT_EQ((type_id)TYPE_INT, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)sizeof(int), (size_t)value_type_size(v)); + EXPECT_EQ((int)23434, (int)value_to_int(v)); value_destroy(v); // Deserialize json string primitive type into value v = serial_deserialize(s, json_string, sizeof(json_string), allocator); - EXPECT_EQ((type_id) TYPE_STRING, (type_id) value_type_id(v)); - EXPECT_EQ((size_t) sizeof(json_string_value), (size_t) value_type_size(v)); - EXPECT_EQ((int) 0, (int) strncmp(value_to_string(v), json_string_value, sizeof(json_string_value) - 1)); + EXPECT_EQ((type_id)TYPE_STRING, (type_id)value_type_id(v)); + EXPECT_EQ((size_t)sizeof(json_string_value), (size_t)value_type_size(v)); + EXPECT_EQ((int)0, (int)strncmp(value_to_string(v), json_string_value, sizeof(json_string_value) - 1)); value_destroy(v); } @@ -277,13 +293,9 @@ TEST_F(serial_test, DefaultConstructor) // MetaCall { static const char hello_world[] = "hello world"; - static const char good_bye[] = "good bye"; - static const size_t hello_world_length = sizeof(hello_world) - 1; - static const size_t good_bye_length = sizeof(good_bye) - 1; - static const char * value_names[TYPE_SIZE] = - { + static const char *value_names[] = { "true", "A", "123", @@ -294,28 +306,35 @@ TEST_F(serial_test, DefaultConstructor) hello_world, "05060708", "[244,6.800000,hello world]", - NULL, /* TODO */ - #if defined(_WIN32) && defined(_MSC_VER) - #if defined(_WIN64) - "0x00000000000A7EF2", - #else - "0x000A7EF2", - #endif - #elif defined(__linux) || defined(__linux__) || defined(__APPLE__) - "0xa7ef2", - #else - "", - #endif - "(null)" + NULL, /* TODO: Map */ +#if defined(_WIN32) && defined(_MSC_VER) + #if defined(_WIN64) + "0x00000000000A7EF2", + #else + "0x000A7EF2", + #endif +#elif defined(__linux) || defined(__linux__) || defined(__APPLE__) + "0xa7ef2", +#else + "", +#endif + NULL, /* TODO: Future */ + NULL, /* TODO: Function */ + "(null)", + NULL, /* TODO: Class */ + NULL, /* TODO: Object */ + NULL, /* TODO: Exception */ + NULL /* TODO: Throwable */ }; - static const char char_array[] = - { + portability_static_assert((int)sizeof(value_names) / sizeof(value_names[0]) == (int)TYPE_SIZE, + "Value names size does not match type size."); + + static const char char_array[] = { 0x05, 0x06, 0x07, 0x08 }; - static const value value_list[] = - { + static const value value_list[] = { value_create_int(244), value_create_double(6.8), value_create_string(hello_world, hello_world_length) @@ -323,6 +342,11 @@ TEST_F(serial_test, DefaultConstructor) static const size_t value_list_size = sizeof(value_list) / sizeof(value_list[0]); + /* TODO: Implement map properly */ + /* + static const char good_bye[] = "good bye"; + static const size_t good_bye_length = sizeof(good_bye) - 1; + static const value value_map_tupla_a[] = { value_create_string(hello_world, hello_world_length), @@ -338,13 +362,16 @@ TEST_F(serial_test, DefaultConstructor) static const value value_map[] = { value_create_array(value_map_tupla_a, sizeof(value_map_tupla_a) / sizeof(value_map_tupla_a[0])), - value_create_array(value_map_tupla_b, sizeof(value_map_tupla_b) / sizeof(value_map_tupla_b[0])), + value_create_array(value_map_tupla_b, sizeof(value_map_tupla_b) / sizeof(value_map_tupla_b[0])) }; static const size_t value_map_size = sizeof(value_map) / sizeof(value_map[0]); + */ - value value_array[TYPE_SIZE] = - { + /* TODO: Implement class properly */ + /* klass cls = class_create(NULL, ACCESSOR_TYPE_STATIC, NULL, NULL); */ + + value value_array[] = { value_create_bool(1), value_create_char('A'), value_create_short(123), @@ -355,42 +382,66 @@ TEST_F(serial_test, DefaultConstructor) value_create_string(hello_world, hello_world_length), value_create_buffer(char_array, sizeof(char_array)), value_create_array(value_list, value_list_size), + /* TODO: Implement map properly */ + NULL, + /* value_create_map(value_map, value_map_size), + */ value_create_ptr((void *)0x000A7EF2), - value_create_null() + /* TODO: Implement class properly */ + NULL, + NULL, + /* + value_create_future(future_create(NULL, NULL)), + value_create_function(function_create(NULL, 0, NULL, NULL)), + */ + value_create_null(), + /* TODO: Implement class properly */ + NULL, + NULL, + /* + value_create_class(cls), + value_create_object(class_new(cls, NULL, NULL, 0)), + */ + /* TODO: Implement exception properly */ + NULL, + NULL }; + portability_static_assert((int)sizeof(value_array) / sizeof(value_array[0]) == (int)TYPE_SIZE, + "Value array size does not match type size."); + const size_t value_names_size = sizeof(value_names) / sizeof(value_names[0]); serial s = serial_create(metacall_name()); for (size_t iterator = 0; iterator < value_names_size; ++iterator) { - /* TODO: Remove this workaround when type map stringification is implemented */ - if (value_type_id(value_array[iterator]) != TYPE_MAP) + /* TODO: Remove this workaround when type map and future/function stringification is implemented */ + if (value_names[iterator] != NULL) { size_t size; - char * buffer = serial_serialize(s, value_array[iterator], &size, allocator); + char *buffer = serial_serialize(s, value_array[iterator], &size, allocator); log_write("metacall", LOG_LEVEL_DEBUG, "(%s == %s)", buffer, value_names[iterator]); - EXPECT_GT((size_t) size, (size_t) 0); + EXPECT_GT((size_t)size, (size_t)0); - EXPECT_EQ((int) 0, (int) strncmp(buffer, value_names[iterator], size - 1)); + EXPECT_EQ((int)0, (int)strncmp(buffer, value_names[iterator], size - 1)); memory_allocator_deallocate(allocator, buffer); - } - value_type_destroy(value_array[iterator]); + value_type_destroy(value_array[iterator]); + } } } // Clear RapidJSON serial - EXPECT_EQ((int) 0, (int) serial_clear(serial_create(rapid_json_name()))); + EXPECT_EQ((int)0, (int)serial_clear(serial_create(rapid_json_name()))); // Clear MetaCall serial - EXPECT_EQ((int) 0, (int) serial_clear(serial_create(metacall_name()))); + EXPECT_EQ((int)0, (int)serial_clear(serial_create(metacall_name()))); // Destroy serial serial_destroy(); diff --git a/source/threading/CMakeLists.txt b/source/threading/CMakeLists.txt new file mode 100644 index 0000000000..830545d85c --- /dev/null +++ b/source/threading/CMakeLists.txt @@ -0,0 +1,188 @@ +# +# Library name and options +# + +# Target name +set(target threading) + +# Exit here if required dependencies are not met +message(STATUS "Lib ${target}") + +# Set API export file and macro +string(TOUPPER ${target} target_upper) +set(export_file "include/${target}/${target}_api.h") +set(export_macro "${target_upper}_API") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(headers + ${include_path}/threading.h + ${include_path}/threading_atomic.h + ${include_path}/threading_thread_id.h + ${include_path}/threading_atomic_ref_count.h + ${include_path}/threading_mutex.h +) + +set(sources + ${source_path}/threading.c + ${source_path}/threading_thread_id.c +) + +if( + (("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") OR + (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") AND ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC"))) AND + (MSVC_VERSION GREATER_EQUAL 1930) +) + set(headers + ${headers} + ${include_path}/threading_atomic_win32.h + ) +endif() + +if(WIN32) + set(sources + ${sources} + ${source_path}/threading_mutex_win32.c + ) +elseif(APPLE) + set(sources + ${sources} + ${source_path}/threading_mutex_macos.c + ) +else() + set(sources + ${sources} + ${source_path}/threading_mutex_pthread.c + ) +endif() + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create library +# + +# Build library +add_library(${target} + ${sources} + ${headers} +) + +# Create namespaced alias +add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# Export library for downstream projects +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) + +# Create API export header +generate_export_header(${target} + EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${export_macro} +) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${PROJECT_BINARY_DIR}/source/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + + PUBLIC + ${DEFAULT_INCLUDE_DIRECTORIES} + + INTERFACE + $ + $ + $ +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${META_PROJECT_NAME}::version + + PUBLIC + ${DEFAULT_LIBRARIES} + + INTERFACE +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${target_upper}_EXPORTS # Export API + + PUBLIC + $<$>:${target_upper}_STATIC_DEFINE> + ${DEFAULT_COMPILE_DEFINITIONS} + + INTERFACE +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_COMPILE_OPTIONS} + + INTERFACE +) + +# +# Linker options +# + +target_link_options(${target} + PRIVATE + + PUBLIC + ${DEFAULT_LINKER_OPTIONS} + + INTERFACE +) diff --git a/source/threading/include/threading/threading.h b/source/threading/include/threading/threading.h new file mode 100644 index 0000000000..f0b7114816 --- /dev/null +++ b/source/threading/include/threading/threading.h @@ -0,0 +1,39 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_H +#define THREADING_H 1 + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +THREADING_API const char *threading_print_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_H */ diff --git a/source/threading/include/threading/threading_atomic.h b/source/threading/include/threading/threading_atomic.h new file mode 100644 index 0000000000..26ba7c45b3 --- /dev/null +++ b/source/threading/include/threading/threading_atomic.h @@ -0,0 +1,75 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_ATOMIC_H +#define THREADING_ATOMIC_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Definitions -- */ + +#if defined(_WIN32) && defined(_MSC_VER) + #if (_MSC_VER < 1930 || defined(__STDC_NO_ATOMICS__)) + /* Before Visual Studio 2022 atomics are not supported, use fallback solution */ + #include + #define THREADING_ATOMIC 1 + #else + #include + #define THREADING_ATOMIC 1 + #endif +#elif defined(__STDC_VERSION__) + #if (__STDC_VERSION__ - 0L) >= 201112L + /* C11 support */ + #if defined(__STDC_NO_ATOMICS__) + #define THREADING_ATOMIC 0 + #error "Using C11 but atomics not supported, check the platform and implement support" + #elif defined __has_include + #if __has_include() + #include + #define THREADING_ATOMIC 1 + #endif + #else + #include + #define THREADING_ATOMIC 1 + #endif + #else + #define THREADING_ATOMIC 0 + #error "C11 is not supported, check the platform and implement support" + #endif +#else + /* TODO: Unknown compiler and platform, check the platform and compiler, then implement support if needed */ + #define THREADING_ATOMIC 0 +#endif + +#if !defined(THREADING_ATOMIC) || (defined(THREADING_ATOMIC) && THREADING_ATOMIC == 0) + #error "Thread atomic support not implemented." +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_ATOMIC_H */ diff --git a/source/threading/include/threading/threading_atomic_ref_count.h b/source/threading/include/threading/threading_atomic_ref_count.h new file mode 100644 index 0000000000..8a1dc1cc45 --- /dev/null +++ b/source/threading/include/threading/threading_atomic_ref_count.h @@ -0,0 +1,158 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_ATOMIC_REF_COUNT_H +#define THREADING_ATOMIC_REF_COUNT_H 1 + +/* -- Headers -- */ + +#include + +#include + +#if defined(__THREAD_SANITIZER__) + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Definitions -- */ + +#define THREADING_ATOMIC_REF_COUNT_MAX UINTMAX_MAX +#define THREADING_ATOMIC_REF_COUNT_MIN 0 + +/* -- Member Data -- */ + +typedef struct +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t count; + threading_mutex_type m; +#else + atomic_uintmax_t count; +#endif +} threading_atomic_ref_count_type; + +/* -- Type Definitions -- */ + +typedef threading_atomic_ref_count_type *threading_atomic_ref_count; + +/* -- Methods -- */ + +static inline void threading_atomic_ref_count_store(threading_atomic_ref_count ref, uintmax_t v) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_store(&ref->m, &ref->count, &v, sizeof(uintmax_t)); +#else + atomic_store(&ref->count, v); +#endif +} + +static inline void threading_atomic_ref_count_initialize(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t init = THREADING_ATOMIC_REF_COUNT_MIN; + + threading_mutex_initialize(&ref->m); + + threading_mutex_store(&ref->m, &ref->count, &init, sizeof(uintmax_t)); +#else + threading_atomic_ref_count_store(ref, THREADING_ATOMIC_REF_COUNT_MIN); +#endif +} + +static inline uintmax_t threading_atomic_ref_count_load(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t result = 0; + + threading_mutex_store(&ref->m, &result, &ref->count, sizeof(uintmax_t)); + + return result; +#else + return atomic_load_explicit(&ref->count, memory_order_relaxed); +#endif +} + +static inline int threading_atomic_ref_count_increment(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_lock(&ref->m); + { + ++ref->count; + } + threading_mutex_unlock(&ref->m); +#else + if (atomic_load_explicit(&ref->count, memory_order_relaxed) == THREADING_ATOMIC_REF_COUNT_MAX) + { + return 1; + } + + atomic_fetch_add_explicit(&ref->count, 1, memory_order_relaxed); +#endif + + return 0; +} + +static inline int threading_atomic_ref_count_decrement(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_lock(&ref->m); + { + --ref->count; + } + threading_mutex_unlock(&ref->m); +#else + if (atomic_load_explicit(&ref->count, memory_order_relaxed) == THREADING_ATOMIC_REF_COUNT_MIN) + { + return 1; + } + + uintmax_t old_ref_count = atomic_fetch_sub_explicit(&ref->count, 1, memory_order_release); + + if (old_ref_count == THREADING_ATOMIC_REF_COUNT_MIN + 1) + { + atomic_thread_fence(memory_order_acquire); + } +#endif + + return 0; +} + +static inline void threading_atomic_ref_count_destroy(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_destroy(&ref->m); +#else + (void)ref; +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_ATOMIC_REF_COUNT_H */ diff --git a/source/threading/include/threading/threading_atomic_win32.h b/source/threading/include/threading/threading_atomic_win32.h new file mode 100644 index 0000000000..00c2055aa5 --- /dev/null +++ b/source/threading/include/threading/threading_atomic_win32.h @@ -0,0 +1,371 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_ATOMIC_WIN32_H +#define THREADING_ATOMIC_WIN32_H 1 + +/* -- Headers -- */ + +#include + +#include +#include +#include + +#ifndef NOMINMAX + #define NOMINMAX +#endif + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool volatile atomic_bool; +typedef char volatile atomic_char; +typedef signed char volatile atomic_schar; +typedef unsigned char volatile atomic_uchar; +typedef short volatile atomic_short; +typedef unsigned short volatile atomic_ushort; +typedef int volatile atomic_int; +typedef unsigned int volatile atomic_uint; +typedef long volatile atomic_long; +typedef unsigned long volatile atomic_ulong; +typedef long long volatile atomic_llong; +typedef unsigned long long volatile atomic_ullong; +typedef size_t volatile atomic_size_t; +typedef ptrdiff_t volatile atomic_ptrdiff_t; +typedef intmax_t volatile atomic_intmax_t; +typedef uintmax_t volatile atomic_uintmax_t; +typedef intptr_t volatile atomic_intptr_t; +typedef uintptr_t volatile atomic_uintptr_t; +typedef uint8_t volatile atomic_uint8_t; +typedef uint16_t volatile atomic_uint16_t; +typedef uint32_t volatile atomic_uint32_t; +typedef uint64_t volatile atomic_uint64_t; +typedef int8_t volatile atomic_int8_t; +typedef int16_t volatile atomic_int16_t; +typedef int32_t volatile atomic_int32_t; +typedef int64_t volatile atomic_int64_t; + +typedef enum memory_order +{ + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +typedef LONG volatile atomic_flag; + +#if _MSC_VER > 1800 && defined(_M_IX86) + #define _InterlockedExchangeAdd64 _InlineInterlockedExchangeAdd64 + #define _InterlockedExchange64 _InlineInterlockedExchange64 +#endif + +#define ATOMIC_BOOL_LOCK_FREE 2 +#define ATOMIC_CHAR_LOCK_FREE 2 +#define ATOMIC_CHAR16_T_LOCK_FREE 2 +#define ATOMIC_CHAR32_T_LOCK_FREE 2 +#define ATOMIC_WCHAR_T_LOCK_FREE 2 +#define ATOMIC_SHORT_LOCK_FREE 2 +#define ATOMIC_INT_LOCK_FREE 2 +#define ATOMIC_LONG_LOCK_FREE 2 +#define ATOMIC_LLONG_LOCK_FREE 2 +#define ATOMIC_POINTER_LOCK_FREE 2 + +#define ATOMIC_FLAG_INIT 0 +#define ATOMIC_VAR_INIT(value) (value) /* TODO: Deprecate for C17, remove in C23 */ + +#define atomic_flag_test_and_set(obj) \ + (bool)_interlockedbittestandset((LONG *)obj, (LONG)0) + +#define atomic_flag_test_and_set_explicit(obj, order) \ + atomic_flag_test_and_set(obj) + +#define atomic_flag_clear(obj) \ + (bool)_interlockedbittestandreset((LONG *)obj, (LONG)0) + +#define atomic_flag_clear_explicit(obj, order) \ + atomic_flag_clear(obj) + +#define atomic_init(obj, desired) (*(obj) = (desired), (void)0) + +#define __atomic_is_lock_free_power_of_2(x) ((x) && !((x) & ((x)-1))) + +#define atomic_is_lock_free(obj) \ + (sizeof(obj) <= 8 && __atomic_is_lock_free_power_of_2(sizeof(obj))) + +#undef __atomic_is_lock_free_power_of_2 + +static inline void atomic_store_explicit8(CHAR volatile *obj, CHAR desired, memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange8(obj, desired); + } + else + { + atomic_init(obj, desired); + } +} + +static inline void atomic_store_explicit16(SHORT volatile *obj, SHORT desired, memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange16(obj, desired); + } + else + { + atomic_init(obj, desired); + } +} + +static inline void atomic_store_explicit32(LONG volatile *obj, LONG desired, memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange(obj, desired); + } + else + { + atomic_init(obj, desired); + } +} + +static inline void atomic_store_explicit64(LONG64 volatile *obj, LONG64 desired, memory_order order) +{ + if (order == memory_order_seq_cst) +#ifdef _M_IX86 + { + _InterlockedExchangeNoFence64(obj, desired); + } + else + { + _InterlockedExchange64(obj, desired); + } +#else + { + _InterlockedExchange64(obj, desired); + } + else + { + atomic_init(obj, desired); + } +#endif +} + +#define atomic_store(obj, desired) \ + atomic_store_explicit(obj, desired, memory_order_seq_cst) + +#define atomic_store_explicit(obj, desired, order) \ + do \ + { \ + if (sizeof *(obj) == 1) \ + { \ + atomic_store_explicit8((CHAR volatile *)(obj), (CHAR)(desired), order); \ + } \ + else if (sizeof *(obj) == 2) \ + { \ + atomic_store_explicit16((SHORT volatile *)(obj), (SHORT)(desired), order); \ + } \ + else if (sizeof *(obj) == 4) \ + { \ + atomic_store_explicit32((LONG volatile *)(obj), (LONG)(desired), order); \ + } \ + else if (sizeof *(obj) == 8) \ + { \ + atomic_store_explicit64((LONG64 volatile *)(obj), (LONG64)(desired), order); \ + } \ + else \ + { \ + abort(); \ + } \ + } while (0) + +#define atomic_load(obj) \ + atomic_fetch_or((obj), 0) + +#define atomic_load_explicit(obj, order) \ + atomic_fetch_or_explicit((obj), 0, order) + +#define atomic_exchange(obj, desired) \ + atomic_exchange_explicit(obj, desired, memory_order_seq_cst) + +#define atomic_exchange_explicit(obj, desired, order) \ + ((sizeof *(obj) == 1) ? _InterlockedExchange8((CHAR volatile *)obj, (CHAR)desired) : \ + (sizeof *(obj) == 2) ? _InterlockedExchange16((SHORT volatile *)obj, (SHORT)desired) : \ + (sizeof *(obj) == 4) ? _InterlockedExchange((LONG volatile *)obj, (LONG)desired) : \ + (sizeof *(obj) == 8) ? _InterlockedExchange64((LONG64 volatile *)obj, (LONG64)desired) : \ + (abort(), 0)) + +static inline bool atomic_compare_exchange8(CHAR volatile *obj, CHAR *expected, CHAR desired) +{ + CHAR previous = _InterlockedCompareExchange8(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +static inline bool atomic_compare_exchange16(SHORT volatile *obj, SHORT *expected, SHORT desired) +{ + SHORT previous = _InterlockedCompareExchange16(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +static inline bool atomic_compare_exchange32(LONG volatile *obj, LONG *expected, LONG desired) +{ + LONG previous = _InterlockedCompareExchange(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +static inline bool atomic_compare_exchange64(LONG64 volatile *obj, LONG64 *expected, LONG64 desired) +{ + LONG64 previous = _InterlockedCompareExchange64(obj, desired, *expected); + bool result = (previous == *expected); + + if (!result) + { + *expected = previous; + } + + return result; +} + +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \ + ((sizeof *(obj) == 1) ? atomic_compare_exchange8((CHAR volatile *)(obj), (CHAR *)(expected), (CHAR)(desired)) : \ + (sizeof *(obj) == 2) ? atomic_compare_exchange16((SHORT volatile *)(obj), (SHORT *)(expected), (SHORT)(desired)) : \ + (sizeof *(obj) == 4) ? atomic_compare_exchange32((LONG volatile *)(obj), (LONG *)(expected), (LONG)(desired)) : \ + (sizeof *(obj) == 8) ? atomic_compare_exchange64((LONG64 volatile *)(obj), (LONG64 *)(expected), (LONG64)(desired)) : \ + (abort(), false)) + +#define atomic_compare_exchange_strong(obj, expected, desired) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, memory_order_seq_cst, memory_order_seq_cst) + +#define atomic_compare_exchange_weak atomic_compare_exchange_strong + +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \ + atomic_compare_exchange_weak((obj), (expected), (desired)) + +#define atomic_fetch_add(obj, arg) \ + atomic_fetch_add_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_add_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedExchangeAdd8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedExchangeAdd16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedExchangeAdd((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedExchangeAdd64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define atomic_fetch_sub(obj, arg) \ + atomic_fetch_sub_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_sub_explicit(obj, arg, order) \ + atomic_fetch_add_explicit(obj, (0 - arg), order) + +#define atomic_fetch_or(obj, arg) \ + atomic_fetch_or_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_or_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedOr8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedOr16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedOr((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedOr64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define atomic_fetch_xor(obj, arg) \ + atomic_fetch_xor_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_xor_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedXor8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedXor16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedXor((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedXor64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define atomic_fetch_and(obj, arg) \ + atomic_fetch_and_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_and_explicit(obj, arg, order) \ + ((sizeof *(obj) == 1) ? _InterlockedAnd8((char volatile *)obj, (char)(arg)) : \ + (sizeof *(obj) == 2) ? _InterlockedAnd16((SHORT volatile *)obj, (SHORT)(arg)) : \ + (sizeof *(obj) == 4) ? _InterlockedAnd((LONG volatile *)obj, (LONG)(arg)) : \ + (sizeof *(obj) == 8) ? _InterlockedAnd64((LONG64 volatile *)obj, (LONG64)(arg)) : \ + (abort(), 0)) + +#define __atomic_compiler_barrier(order) \ + do \ + { \ + if (order > memory_order_consume) \ + { \ + _ReadWriteBarrier(); \ + } \ + } while (0) + +static inline void atomic_thread_fence(memory_order order) +{ + __atomic_compiler_barrier(order); + + if (order == memory_order_seq_cst) + { + MemoryBarrier(); + __atomic_compiler_barrier(order); + } +} + +static inline void atomic_signal_fence(memory_order order) +{ + __atomic_compiler_barrier(order); +} + +#undef __atomic_compiler_barrier + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_ATOMIC_WIN32_H */ diff --git a/source/threading/include/threading/threading_mutex.h b/source/threading/include/threading/threading_mutex.h new file mode 100644 index 0000000000..c9b346c3c8 --- /dev/null +++ b/source/threading/include/threading/threading_mutex.h @@ -0,0 +1,94 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_MUTEX_H +#define THREADING_MUTEX_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Type Definitions -- */ + +#if defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) + #include + #define THREADING_MUTEX_INITIALIZE \ + { \ + 0 \ + } +typedef CRITICAL_SECTION threading_mutex_impl_type; +#elif (defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux) || defined(__gnu_linux__) || defined(__TOS_LINUX__)) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + (defined(bsdi) || defined(__bsdi__)) || \ + defined(__DragonFly__) + #include + #define THREADING_MUTEX_INITIALIZE PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t threading_mutex_impl_type; +#elif (defined(__MACOS__) || defined(macintosh) || defined(Macintosh) || defined(__TOS_MACOS__)) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #include + #define THREADING_MUTEX_INITIALIZE OS_UNFAIR_LOCK_INIT +typedef os_unfair_lock threading_mutex_impl_type; +#else + #error "Platform not supported for mutex implementation" +#endif + +#include + +/* -- Type Definitions -- */ + +typedef threading_mutex_impl_type threading_mutex_type; +typedef threading_mutex_type *threading_mutex; + +/* -- Methods -- */ + +int threading_mutex_initialize(threading_mutex m); + +int threading_mutex_lock(threading_mutex m); + +int threading_mutex_try_lock(threading_mutex m); + +int threading_mutex_unlock(threading_mutex m); + +int threading_mutex_destroy(threading_mutex m); + +static inline int threading_mutex_store(threading_mutex m, void *dest, void *src, size_t size) +{ + if (threading_mutex_lock(m) != 0) + { + return 1; + } + + memcpy(dest, src, size); + + return threading_mutex_unlock(m); +} + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_MUTEX_H */ diff --git a/source/threading/include/threading/threading_thread_id.h b/source/threading/include/threading/threading_thread_id.h new file mode 100644 index 0000000000..af111a4fe0 --- /dev/null +++ b/source/threading/include/threading/threading_thread_id.h @@ -0,0 +1,55 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_THREAD_ID_H +#define THREADING_THREAD_ID_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Headers -- */ + +#include + +/* -- Definitions -- */ + +#define THREAD_ID_INVALID UINT64_MAX + +/* -- Methods -- */ + +/** +* @brief +* Return the current thread id depending on the platform +* +* @return +* Curent thread id casted to an uint64_t +*/ +THREADING_API uint64_t thread_id_get_current(void); + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_THREAD_ID_H */ diff --git a/source/threading/source/threading.c b/source/threading/source/threading.c new file mode 100644 index 0000000000..2d795819f0 --- /dev/null +++ b/source/threading/source/threading.c @@ -0,0 +1,40 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +const char *threading_print_info(void) +{ + static const char threading_info[] = + "Threading Library " METACALL_VERSION "\n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" + +#ifdef ADT_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif + + "\n"; + + return threading_info; +} diff --git a/source/threading/source/threading_mutex_macos.c b/source/threading/source/threading_mutex_macos.c new file mode 100644 index 0000000000..ed47663215 --- /dev/null +++ b/source/threading/source/threading_mutex_macos.c @@ -0,0 +1,62 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + memset(m, 0, sizeof(os_unfair_lock)); + + return 0; +} + +int threading_mutex_lock(threading_mutex m) +{ + os_unfair_lock_lock(m); + + return 0; +} + +int threading_mutex_try_lock(threading_mutex m) +{ + if (os_unfair_lock_trylock(m) == false) + { + return 1; + } + + return 0; +} + +int threading_mutex_unlock(threading_mutex m) +{ + os_unfair_lock_unlock(m); + + return 0; +} + +int threading_mutex_destroy(threading_mutex m) +{ + (void)m; + return 0; +} diff --git a/source/threading/source/threading_mutex_pthread.c b/source/threading/source/threading_mutex_pthread.c new file mode 100644 index 0000000000..4fc161d347 --- /dev/null +++ b/source/threading/source/threading_mutex_pthread.c @@ -0,0 +1,48 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + return pthread_mutex_init(m, NULL); +} + +int threading_mutex_lock(threading_mutex m) +{ + return pthread_mutex_lock(m); +} + +int threading_mutex_try_lock(threading_mutex m) +{ + return pthread_mutex_trylock(m); +} + +int threading_mutex_unlock(threading_mutex m) +{ + return pthread_mutex_unlock(m); +} + +int threading_mutex_destroy(threading_mutex m) +{ + return pthread_mutex_destroy(m); +} diff --git a/source/threading/source/threading_mutex_win32.c b/source/threading/source/threading_mutex_win32.c new file mode 100644 index 0000000000..aefdcfb1e1 --- /dev/null +++ b/source/threading/source/threading_mutex_win32.c @@ -0,0 +1,61 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + InitializeCriticalSection(m); + + return 0; +} + +int threading_mutex_lock(threading_mutex m) +{ + EnterCriticalSection(m); + + return 0; +} + +int threading_mutex_try_lock(threading_mutex m) +{ + if (TryEnterCriticalSection(m) == 0) + { + return 1; + } + + return 0; +} + +int threading_mutex_unlock(threading_mutex m) +{ + LeaveCriticalSection(m); + + return 0; +} + +int threading_mutex_destroy(threading_mutex m) +{ + DeleteCriticalSection(m); + + return 0; +} diff --git a/source/threading/source/threading_thread_id.c b/source/threading/source/threading_thread_id.c new file mode 100644 index 0000000000..0edf1db792 --- /dev/null +++ b/source/threading/source/threading_thread_id.c @@ -0,0 +1,96 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #include +#endif + +#if defined(_WIN32) + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + #include + + #if defined(__MINGW32__) || defined(__MINGW64__) + #include + #endif +#elif defined(__linux__) || \ + (((defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__)) && (!defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12)) + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + #include + #include +#elif ((defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__)) && (defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12) + #include +#elif defined(__FreeBSD__) + #include +#elif defined(__HAIKU__) || defined(__BEOS__) + #include +#else + #error "Unsupported platform thread id" +#endif + +/* -- Methods -- */ + +uint64_t thread_id_get_current(void) +{ +#if defined(_WIN32) + return (uint64_t)GetCurrentThreadId(); +#elif defined(__linux__) + #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) + return (uint64_t)syscall(__NR_gettid); + #else + return (uint64_t)syscall(SYS_gettid); + #endif +#elif (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 + uint64_t thread_id; + + pthread_threadid_np(NULL, &thread_id); + + return (uint64_t)thread_id; + #else + return (uint64_t)syscall(SYS_thread_selfid); + #endif +#elif defined(__FreeBSD__) + long thread_id = 0; + + thr_self(&thread_id); + + return (thread_id < 0) ? 0 : (uint64_t)thread_id; +#elif defined(__HAIKU__) || defined(__BEOS__) + return (uint64_t)thread_get_current_thread_id(); +#else + return THREAD_ID_INVALID; +#endif +} diff --git a/source/version/CMakeLists.txt b/source/version/CMakeLists.txt index 4326f51841..ff0bed815d 100644 --- a/source/version/CMakeLists.txt +++ b/source/version/CMakeLists.txt @@ -1,9 +1,3 @@ -# -# External dependencies -# - -# find_package(THIRDPARTY REQUIRED) - # # Library name and options # @@ -16,7 +10,6 @@ message(STATUS "Lib ${target}") # Set API export file and macro string(TOUPPER ${target} target_upper) -set(feature_file "include/${target}/${target}_features.h") set(export_file "include/${target}/${target}_api.h") set(export_macro "${target_upper}_API") @@ -39,22 +32,20 @@ include(SecurityFlags) string(TOUPPER ${META_PROJECT_NAME} META_PROJECT_NAME_UPPER) string(TOLOWER ${META_PROJECT_NAME} META_PROJECT_NAME_LOWER) -set(template_include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") -set(template_source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") - -set(include_path "${CMAKE_BINARY_DIR}/source/include/${META_PROJECT_NAME_LOWER}") -set(source_path "${CMAKE_BINARY_DIR}/source/${META_PROJECT_NAME_LOWER}/source") +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(include_binary_path "${CMAKE_BINARY_DIR}/source/include/${META_PROJECT_NAME_LOWER}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") # Generate version-header -configure_file(${template_include_path}/version.h.in ${include_path}/${META_PROJECT_NAME_LOWER}_version.h) -configure_file(${template_source_path}/version.c.in ${source_path}/${META_PROJECT_NAME_LOWER}_version.c) +configure_file(${include_path}/version.h.in ${include_binary_path}/${META_PROJECT_NAME_LOWER}_version.h) set(headers - ${include_path}/${META_PROJECT_NAME_LOWER}_version.h + ${include_binary_path}/${META_PROJECT_NAME_LOWER}_version.h + ${include_path}/version.h ) set(sources - ${source_path}/${META_PROJECT_NAME_LOWER}_version.c + ${source_path}/version.c ) # Group source files @@ -62,8 +53,6 @@ set(header_group "Header Files (API)") set(source_group "Source Files") source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" ${header_group} ${headers}) -source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" - ${source_group} ${sources}) # # Create library @@ -71,38 +60,15 @@ source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" # Build library add_library(${target} - ${sources} ${headers} + ${sources} ) # Create namespaced alias add_library(${META_PROJECT_NAME}::${target} ALIAS ${target}) # Export library for downstream projects -if(NOT OPTION_BUILD_DIST_LIBS) - export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) -endif() - -# Create feature detection header -# Compilers: https://cmake.org/cmake/help/v3.1/variable/CMAKE_LANG_COMPILER_ID.html#variable:CMAKE_%3CLANG%3E_COMPILER_ID -# Feature: https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html - -# Check for availability of module; use pre-generated version if not found -if (WriterCompilerDetectionHeaderFound) - write_compiler_detection_header( - FILE ${feature_file} - PREFIX ${target_upper} - COMPILERS AppleClang Clang GNU MSVC - FEATURES cxx_alignas cxx_alignof cxx_constexpr cxx_final cxx_noexcept cxx_nullptr cxx_sizeof_member cxx_thread_local - VERSION 3.2 - ) -else() - file( - COPY ${PROJECT_SOURCE_DIR}/codegeneration/${target}_features.h - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/${target} - USE_SOURCE_PERMISSIONS - ) -endif() +export(TARGETS ${target} NAMESPACE ${META_PROJECT_NAME}:: FILE ${PROJECT_BINARY_DIR}/cmake/${target}/${target}-export.cmake) # Create API export header generate_export_header(${target} @@ -130,6 +96,7 @@ target_include_directories(${target} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include + PUBLIC ${DEFAULT_INCLUDE_DIRECTORIES} @@ -158,6 +125,7 @@ target_link_libraries(${target} target_compile_definitions(${target} PRIVATE + ${target_upper}_EXPORTS # Export API PUBLIC $<$>:${target_upper}_STATIC_DEFINE> @@ -183,7 +151,7 @@ target_compile_options(${target} # Linker options # -target_link_libraries(${target} +target_link_options(${target} PRIVATE PUBLIC @@ -191,17 +159,3 @@ target_link_libraries(${target} INTERFACE ) - -# -# Deployment -# - -# Library -if(NOT OPTION_BUILD_DIST_LIBS) - install(TARGETS ${target} - EXPORT "${target}-export" COMPONENT dev - RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT runtime - LIBRARY DESTINATION ${INSTALL_SHARED} COMPONENT runtime - ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT dev - ) -endif() diff --git a/source/version/include/version/version.h b/source/version/include/version/version.h new file mode 100644 index 0000000000..eae5523e89 --- /dev/null +++ b/source/version/include/version/version.h @@ -0,0 +1,36 @@ +/* + * CMake Versioning Utility by Parra Studios + * A template for generating versioning utilities. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef VERSION_H +#define VERSION_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +VERSION_API const char *version_print_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VERSION_H */ diff --git a/source/version/include/version/version.h.in b/source/version/include/version/version.h.in index 3d99f512bc..69341f7b5b 100644 --- a/source/version/include/version/version.h.in +++ b/source/version/include/version/version.h.in @@ -2,7 +2,7 @@ * CMake Versioning Utility by Parra Studios * A template for generating versioning utilities. * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,18 +21,6 @@ #ifndef ${META_PROJECT_NAME_UPPER}_VERSION_H #define ${META_PROJECT_NAME_UPPER}_VERSION_H 1 -/* -- Headers -- */ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* -- Headers -- */ - -#include - /* -- Definitions -- */ #define ${META_PROJECT_NAME_UPPER}_PROJECT_NAME "@META_PROJECT_NAME@" @@ -53,89 +41,4 @@ extern "C" { #define ${META_PROJECT_NAME_UPPER}_VERSION "@META_VERSION@" #define ${META_PROJECT_NAME_UPPER}_NAME_VERSION "@META_NAME_VERSION@" -/* -- Member Data -- */ - -struct ${META_PROJECT_NAME}_version_type -{ - unsigned int major; - unsigned int minor; - unsigned int patch; - const char * revision; - const char * str; - const char * name; -}; - -/* -- Methods -- */ - -/** -* @brief -* Provide the module version struct -* -* @return -* Static struct containing unpacked version -*/ -VERSION_API const void * ${META_PROJECT_NAME}_version(void); - -/** -* @brief -* Provide the module version hexadecimal value -* with format 0xMMIIPPPP where M is @major, -* I is @minor and P is @patch -* -* @param[in] major -* Unsigned integer representing major version -* -* @param[in] minor -* Unsigned integer representing minor version -* -* @param[in] patch -* Unsigned integer representing patch version -* -* @return -* Hexadecimal integer containing packed version -*/ -VERSION_API uint32_t ${META_PROJECT_NAME}_version_hex_make(unsigned int major, unsigned int minor, unsigned int patch); - -/** -* @brief -* Provide the module version hexadecimal value -* with format 0xMMIIPPPP where M is major, -* I is minor and P is patch -* -* @return -* Hexadecimal integer containing packed version -*/ -VERSION_API uint32_t ${META_PROJECT_NAME}_version_hex(void); - -/** -* @brief -* Provide the module version string -* -* @return -* Static string containing module version -*/ -VERSION_API const char * ${META_PROJECT_NAME}_version_str(void); - -/** -* @brief -* Provide the module version revision string -* -* @return -* Static string containing module version revision -*/ -VERSION_API const char * ${META_PROJECT_NAME}_version_revision(void); - -/** -* @brief -* Provide the module version name -* -* @return -* Static string containing module version name -*/ -VERSION_API const char * ${META_PROJECT_NAME}_version_name(void); - -#ifdef __cplusplus -} -#endif - #endif /* ${META_PROJECT_NAME_UPPER}_VERSION_H */ diff --git a/source/version/source/version.c b/source/version/source/version.c new file mode 100644 index 0000000000..95cec396c5 --- /dev/null +++ b/source/version/source/version.c @@ -0,0 +1,40 @@ +/* + * CMake Versioning Utility by Parra Studios + * A template for generating versioning utilities. + * + * Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +const char *version_print_info(void) +{ + static const char version_info[] = + "Version Library " METACALL_VERSION "\n" + "Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia \n" + +#ifdef ADT_STATIC_DEFINE + "Compiled as static library type" +#else + "Compiled as shared library type" +#endif + + "\n"; + + return version_info; +} diff --git a/source/version/source/version.c.in b/source/version/source/version.c.in deleted file mode 100644 index d5cec24e0c..0000000000 --- a/source/version/source/version.c.in +++ /dev/null @@ -1,78 +0,0 @@ -/* - * CMake Versioning Utility by Parra Studios - * A template for generating versioning utilities. - * - * Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* -- Headers -- */ - -#include <${META_PROJECT_NAME_LOWER}/${META_PROJECT_NAME_LOWER}_version.h> - -/* -- Methods -- */ - -const void * ${META_PROJECT_NAME}_version_data() -{ - static const struct ${META_PROJECT_NAME}_version_type version = - { - ${META_PROJECT_NAME_UPPER}_VERSION_MAJOR_ID, - ${META_PROJECT_NAME_UPPER}_VERSION_MINOR_ID, - ${META_PROJECT_NAME_UPPER}_VERSION_PATCH_ID, - ${META_PROJECT_NAME_UPPER}_VERSION_REVISION, - ${META_PROJECT_NAME_UPPER}_VERSION, - ${META_PROJECT_NAME_UPPER}_NAME_VERSION - }; - - return &version; -} - -uint32_t ${META_PROJECT_NAME}_version_hex_make(unsigned int major, unsigned int minor, unsigned int patch) -{ - const uint32_t version_hex = (major << 0x18) + (minor << 0x10) + patch; - - return version_hex; -} - -uint32_t ${META_PROJECT_NAME}_version_hex() -{ - static const uint32_t version_hex = - (${META_PROJECT_NAME_UPPER}_VERSION_MAJOR_ID << 0x18) + - (${META_PROJECT_NAME_UPPER}_VERSION_MINOR_ID << 0x10) + - ${META_PROJECT_NAME_UPPER}_VERSION_PATCH_ID; - - return version_hex; -} - -const char * ${META_PROJECT_NAME}_version_str() -{ - static const char version_string[] = ${META_PROJECT_NAME_UPPER}_VERSION; - - return version_string; -} - -const char * ${META_PROJECT_NAME}_version_revision() -{ - static const char version_revision[] = ${META_PROJECT_NAME_UPPER}_VERSION_REVISION; - - return version_revision; -} - -const char * ${META_PROJECT_NAME}_version_name() -{ - static const char version_name[] = ${META_PROJECT_NAME_UPPER}_NAME_VERSION; - - return version_name; -} diff --git a/tools/base/Dockerfile b/tools/base/Dockerfile deleted file mode 100644 index f7e898368b..0000000000 --- a/tools/base/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -# -# MetaCall Library by Parra Studios -# Docker image infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Configure MetaCall Depends node image -ARG METACALL_BASE_IMAGE - -# MetaCall Depends node image -FROM metacall/core:deps_node AS deps_node - -# MetaCall Depends base image -FROM ${METACALL_BASE_IMAGE} AS deps - -# Image descriptor -LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ - copyright.address="vic798@gmail.com" \ - maintainer.name="Vicente Eduardo Ferrer Garcia" \ - maintainer.address="vic798@gmail.com" \ - vendor="MetaCall Inc." \ - version="0.1" - -# Arguments -ARG METACALL_PATH -ARG METACALL_TOOLS_PATH - -# Environment variables -ENV DEBIAN_FRONTEND=noninteractive - -# Work around https://github.com/dotnet/cli/issues/1582 until Docker releases a -# fix (https://github.com/docker/docker/issues/20818). This workaround allows -# the container to be run with the default seccomp Docker settings by avoiding -# the restart_syscall made by LTTng which causes a failed assertion. -ENV LTTNG_UST_REGISTER_TIMEOUT=0 - -# Trigger the population of the local NuGet package cache -ENV NUGET_XMLDOC_MODE=skip - -# Define working directory -WORKDIR $METACALL_PATH - -# Copy MetaCall NodeJS dependencies -COPY --from=deps_node /usr/local/lib/libnode.so.* /usr/local/lib/ - -# Copy MetaCall tool dependecies -COPY tools/metacall-environment.sh tools/nobuildtest.patch $METACALL_TOOLS_PATH/ - -# Install MetaCall and runtimes then build -ARG METACALL_INSTALL_OPTIONS - -RUN chmod 500 $METACALL_TOOLS_PATH/metacall-environment.sh \ - && $METACALL_TOOLS_PATH/metacall-environment.sh ${METACALL_INSTALL_OPTIONS} \ - && rm -rf $METACALL_PATH diff --git a/tools/base/hooks/build b/tools/base/hooks/build deleted file mode 100755 index d54059aed9..0000000000 --- a/tools/base/hooks/build +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build deps image -docker build \ - --cache-from registry.hub.docker.com/metacall/core:deps_node \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_BASE_IMAGE=$METACALL_BASE_IMAGE" \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_TOOLS_PATH=$METACALL_PATH/tools" \ - --build-arg "METACALL_INSTALL_OPTIONS=$METACALL_INSTALL_OPTIONS" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/base/hooks/env b/tools/base/hooks/env deleted file mode 100755 index 917cdcc22b..0000000000 --- a/tools/base/hooks/env +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Base arguments -METACALL_INSTALL_OPTIONS="root base python ruby netcore v8rep51 nodejs rapidjson funchook swig" # pack and coverage not needed in DockerHub - -# Base environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip - -# Override docker image -IMAGE_NAME=metacall/core:deps diff --git a/tools/cli/.dockerignore b/tools/cli/.dockerignore new file mode 100644 index 0000000000..1d085cacc9 --- /dev/null +++ b/tools/cli/.dockerignore @@ -0,0 +1 @@ +** diff --git a/tools/cli/Dockerfile b/tools/cli/Dockerfile index bfb570e375..0da28539b1 100644 --- a/tools/cli/Dockerfile +++ b/tools/cli/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,17 +20,6 @@ # MetaCall Depends builder image FROM metacall/core:dev AS builder -# Image descriptor -LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ - copyright.address="vic798@gmail.com" \ - maintainer.name="Vicente Eduardo Ferrer Garcia" \ - maintainer.address="vic798@gmail.com" \ - vendor="MetaCall Inc." \ - version="0.1" - -# Environment variables -ENV DEBIAN_FRONTEND=noninteractive - # MetaCall image FROM metacall/core:runtime AS cli @@ -48,8 +37,9 @@ ENV LOADER_LIBRARY_PATH=/usr/local/lib \ CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json \ SERIAL_LIBRARY_PATH=/usr/local/lib \ DETOUR_LIBRARY_PATH=/usr/local/lib \ - PORT_LIBRARY_PATH=/usr/local/lib \ - DEBIAN_FRONTEND=noninteractive + DEBIAN_FRONTEND=noninteractive \ + NODE_PATH=/usr/local/lib/node_modules \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $LOADER_SCRIPT_PATH diff --git a/tools/cli/hooks/build b/tools/cli/hooks/build deleted file mode 100755 index b636c71642..0000000000 --- a/tools/cli/hooks/build +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build core image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/cli/hooks/env b/tools/cli/hooks/env deleted file mode 100755 index 0cc61213cd..0000000000 --- a/tools/cli/hooks/env +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# CLI environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip -LOADER_LIBRARY_PATH=/usr/local/lib -LOADER_SCRIPT_PATH=/usr/local/scripts -CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json -SERIAL_LIBRARY_PATH=/usr/local/lib -DETOUR_LIBRARY_PATH=/usr/local/lib - -# Override docker image -IMAGE_NAME=metacall/core:cli diff --git a/tools/core/Dockerfile b/tools/core/Dockerfile deleted file mode 100644 index c6cd74c5a2..0000000000 --- a/tools/core/Dockerfile +++ /dev/null @@ -1,95 +0,0 @@ -# -# MetaCall Library by Parra Studios -# Docker image infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Configure MetaCall base image -ARG METACALL_BASE_IMAGE - -# MetaCall Depends builder image -FROM metacall/core:dev AS builder - -# Image descriptor -LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ - copyright.address="vic798@gmail.com" \ - maintainer.name="Vicente Eduardo Ferrer Garcia" \ - maintainer.address="vic798@gmail.com" \ - vendor="MetaCall Inc." \ - version="0.1" - -# Arguments -ARG METACALL_PATH - -# Environment variables -ENV DEBIAN_FRONTEND=noninteractive - -# Define working directory -WORKDIR $METACALL_PATH - -# MetaCall image -FROM ${METACALL_BASE_IMAGE} AS core - -# Image descriptor -LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ - copyright.address="vic798@gmail.com" \ - maintainer.name="Vicente Eduardo Ferrer Garcia" \ - maintainer.address="vic798@gmail.com" \ - vendor="MetaCall Inc." \ - version="0.1" - -# Arguments -ARG METACALL_PATH - -# Environment variables -ENV LOADER_LIBRARY_PATH=/usr/local/lib \ - LOADER_SCRIPT_PATH=/usr/local/scripts \ - CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json \ - SERIAL_LIBRARY_PATH=/usr/local/lib \ - DETOUR_LIBRARY_PATH=/usr/local/lib \ - PORT_LIBRARY_PATH=/usr/local/lib \ - DEBIAN_FRONTEND=noninteractive - -# Define working directory -WORKDIR $METACALL_PATH - -# Copy MetaCall dependecies -COPY tools/metacall-runtime.sh $METACALL_PATH - -# Configure MetaCall runtime tool script -ARG METACALL_RUNTIME_OPTIONS - -# Install runtimes -RUN mkdir -p /usr/local/scripts \ - && chmod 500 $METACALL_PATH/metacall-runtime.sh \ - && $METACALL_PATH/metacall-runtime.sh ${METACALL_RUNTIME_OPTIONS} \ - && rm -rf $METACALL_PATH/metacall-runtime.sh - -# Copy libraries from builder -COPY --from=builder /usr/local/lib/*.so /usr/local/lib/*.so* /usr/local/lib/*.dll /usr/local/lib/*.js /usr/local/lib/*.node /usr/local/lib/ - -# Copy node dependencies (and port) from builder -COPY --from=builder /usr/local/lib/node_modules/ /usr/local/lib/node_modules/ - -# Copy python dependencies and port from builder -COPY --from=builder /usr/local/lib/python3.5/dist-packages/metacall/__init__.py /usr/local/lib/python3.5/dist-packages/metacall/ -COPY --from=builder /usr/local/lib/python3.5/dist-packages/metacall/__pycache__/__init__.cpython-35.pyc /usr/local/lib/python3.5/dist-packages/metacall/__pycache__/ - -# Copy headers from builder -COPY --from=builder /usr/local/include/metacall /usr/local/include/metacall - -# Copy configuration from builder -COPY --from=builder /usr/local/share/metacall/configurations/* /usr/local/share/metacall/configurations/ diff --git a/tools/core/hooks/build b/tools/core/hooks/build deleted file mode 100755 index 1abc3edb39..0000000000 --- a/tools/core/hooks/build +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build core image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_BASE_IMAGE=$METACALL_BASE_IMAGE" \ - --build-arg "METACALL_RUNTIME_OPTIONS=$METACALL_RUNTIME_OPTIONS" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/core/hooks/env b/tools/core/hooks/env deleted file mode 100755 index 516c124ef8..0000000000 --- a/tools/core/hooks/env +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Core arguments -METACALL_RUNTIME_OPTIONS="root base python ruby netcore v8 nodejs ports clean" - -# Core environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip -LOADER_LIBRARY_PATH=/usr/local/lib -LOADER_SCRIPT_PATH=/usr/local/scripts -CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json -SERIAL_LIBRARY_PATH=/usr/local/lib -DETOUR_LIBRARY_PATH=/usr/local/lib - -# Override docker image -IMAGE_NAME=metacall/core:runtime diff --git a/tools/base/.dockerignore b/tools/deps/.dockerignore similarity index 100% rename from tools/base/.dockerignore rename to tools/deps/.dockerignore diff --git a/tools/deps/Dockerfile b/tools/deps/Dockerfile new file mode 100644 index 0000000000..342bef8de3 --- /dev/null +++ b/tools/deps/Dockerfile @@ -0,0 +1,56 @@ +# +# MetaCall Library by Parra Studios +# Docker image infrastructure for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configure MetaCall Depends node image +ARG METACALL_BASE_IMAGE + +# MetaCall Depends base image +FROM ${METACALL_BASE_IMAGE} AS deps + +# Image descriptor +LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ + copyright.address="vic798@gmail.com" \ + maintainer.name="Vicente Eduardo Ferrer Garcia" \ + maintainer.address="vic798@gmail.com" \ + vendor="MetaCall Inc." \ + version="0.1" + +# Arguments +ARG METACALL_PATH +ARG METACALL_TOOLS_PATH + +# Environment variables +ENV DEBIAN_FRONTEND=noninteractive \ + LTTNG_UST_REGISTER_TIMEOUT=0 \ + NUGET_XMLDOC_MODE=skip \ + DOTNET_CLI_TELEMETRY_OPTOUT=true + +# Define working directory +WORKDIR $METACALL_PATH + +# Copy MetaCall tool dependecies +COPY tools/metacall-environment.sh tools/nobuildtest.patch $METACALL_TOOLS_PATH/ + +# Install MetaCall and runtimes then build +ARG METACALL_BUILD_TYPE +ARG METACALL_INSTALL_OPTIONS + +RUN chmod 500 $METACALL_TOOLS_PATH/metacall-environment.sh \ + && $METACALL_TOOLS_PATH/metacall-environment.sh ${METACALL_BUILD_TYPE} ${METACALL_INSTALL_OPTIONS} \ + && rm -rf $METACALL_PATH diff --git a/tools/dev/.dockerignore b/tools/dev/.dockerignore index 442125e2a4..ab1f6c4a43 100644 --- a/tools/dev/.dockerignore +++ b/tools/dev/.dockerignore @@ -6,6 +6,7 @@ !source/** !tools/metacall-configure.sh !tools/metacall-build.sh +!.clang-format !CMakeLists.txt !metacall-config.cmake.in !metacall-config-version.cmake.in @@ -13,4 +14,6 @@ !DESCRIPTION !LICENSE !README.md +!VERSION !WELCOME +source/loaders/rs_loader/rust/target diff --git a/tools/dev/Dockerfile b/tools/dev/Dockerfile index 84513cd961..ec35dfde43 100644 --- a/tools/dev/Dockerfile +++ b/tools/dev/Dockerfile @@ -2,7 +2,7 @@ # MetaCall Library by Parra Studios # Docker image infrastructure for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,8 +37,9 @@ ENV LOADER_LIBRARY_PATH=$METACALL_PATH/build \ CONFIGURATION_PATH=$METACALL_PATH/build/configurations/global.json \ SERIAL_LIBRARY_PATH=$METACALL_PATH/build \ DETOUR_LIBRARY_PATH=$METACALL_PATH/build \ - PORT_LIBRARY_PATH=$METACALL_PATH/build \ - DEBIAN_FRONTEND=noninteractive + DEBIAN_FRONTEND=noninteractive \ + NODE_PATH=/usr/lib/node_modules \ + DOTNET_CLI_TELEMETRY_OPTOUT=true # Define working directory WORKDIR $METACALL_PATH diff --git a/tools/dev/hooks/build b/tools/dev/hooks/build deleted file mode 100755 index 1c88988733..0000000000 --- a/tools/dev/hooks/build +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build dev image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_CORE_BUILD_TYPE=$METACALL_CORE_BUILD_TYPE" \ - --build-arg "METACALL_BUILD_OPTIONS=$METACALL_BUILD_OPTIONS" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/dev/hooks/env b/tools/dev/hooks/env deleted file mode 100755 index 5dfacfc5dd..0000000000 --- a/tools/dev/hooks/env +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Core arguments -METACALL_BUILD_OPTIONS="root python ruby netcore v8 nodejs examples distributable tests scripts ports dynamic install" # pack and coverage not needed in DockerHub - -# Core environment variables -DEBIAN_FRONTEND=noninteractive -LTTNG_UST_REGISTER_TIMEOUT=0 -NUGET_XMLDOC_MODE=skip -LOADER_LIBRARY_PATH=$METACALL_PATH/build -LOADER_SCRIPT_PATH=$METACALL_PATH/build/scripts -CONFIGURATION_PATH=$METACALL_PATH/build/configurations/global.json -SERIAL_LIBRARY_PATH=$METACALL_PATH/build -DETOUR_LIBRARY_PATH=$METACALL_PATH/build - -# Override docker image -IMAGE_NAME=metacall/core:dev diff --git a/tools/metacall-benchmarks-merge.py b/tools/metacall-benchmarks-merge.py new file mode 100644 index 0000000000..30270e269f --- /dev/null +++ b/tools/metacall-benchmarks-merge.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +import json +import os +import sys + +# Validate arguments +if len(sys.argv) != 2: + print('Invalid number of arguments, you should pass the location of the benchmarks.') + print('Usage: python3 metacall-benchmarks-merge.py ${CMAKE_BINARY_DIR}/benchmarks') + exit(1) + +# Validate the benchmark path +if not os.path.isdir(sys.argv[1]): + print('The directory \'' + sys.argv[1] + '\' does not exist or is not a valid path.') + exit(2) + +# Search all benchmarks and merge them +output = {} + +for file in os.listdir(sys.argv[1]): + if file.endswith('.json'): + f = open(os.path.join(sys.argv[1], file), 'r') + + # Sanitize the data (https://github.com/google/benchmark/issues/784) + # '-Infinity', 'Infinity', 'NaN' + def sanitize(arg): + c = { + '-Infinity': sys.float_info.min, # -float('inf') + 'Infinity': sys.float_info.max, # float('inf') + 'NaN': 0 # float('nan') + } + # TODO: Eventually solve this from the root of problem in Windows + print('Warning: Got value "' + arg + '" in the test ' + file + ', review why it is failing') + return c[arg] + + data = json.loads(f.read(), parse_constant=sanitize) + + if not output: + output = data + else: + for benchmark in data['benchmarks']: + output['benchmarks'].append(benchmark) + + f.close() + +# Check if we found data +if not output: + print('The directory \'' + sys.argv[1] + '\' does not contain any benchmark.') + exit(3) + +# Rename the binary file +output['context']['executable'] = 'benchmarks' + +# Store the result +dest = os.path.join(sys.argv[1], 'metacall-benchmarks.json') +f = open(dest, 'w+') +json.dump(output, f, indent = 4) +f.close() diff --git a/tools/metacall-build.ps1 b/tools/metacall-build.ps1 new file mode 100755 index 0000000000..ec95a2dd85 --- /dev/null +++ b/tools/metacall-build.ps1 @@ -0,0 +1,123 @@ +<# +# MetaCall Build PowerShell Script by Parra Studios +# Build and install powershell script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#> + +$Global:BUILD_TYPE = 'Release' +$Global:BUILD_TESTS = 0 +$Global:BUILD_BENCHMARKS = 0 +$Global:BUILD_COVERAGE = 0 +$Global:BUILD_INSTALL = 0 + +$Global:PROGNAME = $(Get-Item $PSCommandPath).Basename +$Global:Arguments = $args + +function Sub-Options { + for ($i = 0; $i -lt $Arguments.Length; $i++) { + $option = $Arguments[$i] + if ("$option" -eq "debug") { + echo "Build all scripts in debug mode" + $Global:BUILD_TYPE = 'Debug' + } + if ("$option" -eq "release") { + echo "Build all scripts in release mode" + $Global:BUILD_TYPE = 'Release' + } + if ("$option" -eq "relwithdebinfo") { + echo "Build all scripts in release mode with debug symbols" + $Global:BUILD_TYPE = 'RelWithDebInfo' + } + if ("$option" -eq "tests") { + echo "Build and run all tests" + $Global:BUILD_TESTS = 1 + } + if ("$option" -eq "benchmarks") { + echo "Build and run all benchmarks" + $Global:BUILD_BENCHMARKS = 1 + } + if ("$option" -eq "install") { + echo "Install all libraries" + $Global:BUILD_INSTALL = 1 + } + } +} + +function Sub-Build { + $Global:ExitCode = 0 + + # Build the project + echo "Building MetaCall..." + cmake --build . "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" --config $BUILD_TYPE + + if (-not $?) { + $RecentExitCode = $LASTEXITCODE + echo "Failure in build with exit code: $RecentExitCode" + + $Global:ExitCode = $RecentExitCode + Exit $ExitCode + } + + # Tests + if (($BUILD_TESTS -eq 1) -or ($BUILD_BENCHMARKS -eq 1) -or ($BUILD_COVERAGE -eq 1)) { + echo "Running the tests..." + ctest "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" --timeout 5400 --output-on-failure -C $BUILD_TYPE + + if (-not $?) { + $RecentExitCode = $LASTEXITCODE + echo "Failure in tests with exit code: $RecentExitCode" + + $Global:ExitCode = $RecentExitCode + Exit $ExitCode + } + } + + # Install + if ($BUILD_INSTALL -eq 1) { + echo "Building and installing MetaCall..." + cmake --build . --target install --config $BUILD_TYPE + + if (-not $?) { + $RecentExitCode = $LASTEXITCODE + echo "Failure in install with exit code: $RecentExitCode" + + $Global:ExitCode = $RecentExitCode + Exit $ExitCode + } + } + + Exit $ExitCode +} + +function Sub-Help { + echo "Usage: $PROGNAME list of options" + echo "Options:" + echo " debug | release | relwithdebinfo: build type" + echo " tests: build and run all tests" + echo " install: install all libraries" + echo "" +} + +switch($args.length) { + 0 { + Sub-Help + Break + } + Default { + Sub-Options + Sub-Build + } +} diff --git a/tools/metacall-build.sh b/tools/metacall-build.sh index cba2109812..74826e22f2 100755 --- a/tools/metacall-build.sh +++ b/tools/metacall-build.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. +# MetaCall Build Shell Script by Parra Studios +# Build and install shell script utility for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,21 +19,24 @@ # limitations under the License. # -RUN_AS_ROOT=0 -SUDO_CMD=sudo +set -euxo + BUILD_TYPE=Release BUILD_TESTS=0 +BUILD_BENCHMARKS=0 BUILD_COVERAGE=0 BUILD_INSTALL=0 +# Check out for sudo +if [ "`id -u`" = '0' ]; then + SUDO_CMD="" +else + SUDO_CMD=sudo +fi + sub_options() { for option in "$@" do - if [ "$option" = 'root' ]; then - echo "Running build script as root" - RUN_AS_ROOT=1 - SUDO_CMD="" - fi if [ "$option" = 'debug' ]; then echo "Build all scripts in debug mode" BUILD_TYPE=Debug @@ -50,6 +53,10 @@ sub_options() { echo "Build and run all tests" BUILD_TESTS=1 fi + if [ "$option" = 'benchmarks' ]; then + echo "Build and run all benchmarks" + BUILD_BENCHMARKS=1 + fi if [ "$option" = 'coverage' ]; then echo "Build coverage reports" BUILD_COVERAGE=1 @@ -63,32 +70,34 @@ sub_options() { sub_build() { - # Make without distributable - make -k -j$(getconf _NPROCESSORS_ONLN) + # Build the project + make -j$(getconf _NPROCESSORS_ONLN) # Tests (coverage needs to run the tests) - if [ $BUILD_TESTS = 1 ] || [ $BUILD_COVERAGE = 1 ]; then - ctest -VV -C $BUILD_TYPE + if [ $BUILD_TESTS = 1 ] || [ $BUILD_BENCHMARKS=1 ] || [ $BUILD_COVERAGE = 1 ]; then + ctest -j$(getconf _NPROCESSORS_ONLN) --timeout 5400 --output-on-failure --test-output-size-failed 3221000000 -C $BUILD_TYPE fi # Coverage if [ $BUILD_COVERAGE = 1 ]; then - # TODO: Remove -k, solve coverage issues - make -k gcov - make -k lcov - make -k lcov-genhtml + ctest -j$(getconf _NPROCESSORS_ONLN) --timeout 5400 -T Coverage + gcovr -r ../source/ . --html-details coverage.html fi # Install if [ $BUILD_INSTALL = 1 ]; then - $SUDO_CMD make install + if [ "$SUDO_CMD" = "" ]; then + make install + else + # Needed for rustup in order to install rust loader properly + $SUDO_CMD HOME="$HOME" make install + fi fi } sub_help() { echo "Usage: `basename "$0"` list of options" echo "Options:" - echo " root: build being run by root" echo " debug | release | relwithdebinfo: build type" echo " tests: build and run all tests" echo " coverage: build coverage reports" diff --git a/tools/metacall-clear.sh b/tools/metacall-clear.sh deleted file mode 100755 index 6d9c5cea6b..0000000000 --- a/tools/metacall-clear.sh +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Configuration Environment Bash Script by Parra Studios -# Remove all packages and unused data from MetaCall building and testing. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -ROOT_DIR=$(pwd) - -RUN_AS_ROOT=0 -SUDO_CMD=sudo -CLEAR_APT=0 -CLEAR_RAPIDJSON=0 -CLEAR_FUNCHOOK=0 -CLEAR_PYTHON=0 -CLEAR_RUBY=0 -CLEAR_NETCORE=0 -CLEAR_V8=0 -CLEAR_NODEJS=0 -CLEAR_SWIG=0 -CLEAR_PACK=0 -CLEAR_COVERAGE=0 -SHOW_HELP=0 -PROGNAME=$(basename $0) - -# Base packages -sub_apt(){ - echo "clean apt of C build" - $SUDO_CMD apt-get -y remove --purge build-essential git cmake wget apt-utils apt-transport-https gnupg dirmngr ca-certificates - $SUDO_CMD apt-get -y autoclean - $SUDO_CMD apt-get -y autoremove -} - -# RapidJSON -sub_rapidjson(){ - echo "clean rapidJSON" - $SUDO_CMD rm -rf /usr/local/lib/cmake - $SUDO_CMD rm -rf /usr/local/include/rapidjson -} - -# FuncHook -sub_funchook(){ - echo "clean funchook" -} - -# Python -sub_python(){ - echo "clean python" - /usr/bin/yes | $SUDO_CMD pip3 uninstall django - /usr/bin/yes | $SUDO_CMD pip3 uninstall requests - /usr/bin/yes | $SUDO_CMD pip3 uninstall rsa - /usr/bin/yes | $SUDO_CMD pip3 uninstall joblib -} - -# Ruby -sub_ruby(){ - echo "clean ruby" - # TODO: Review conflict with NodeJS (currently rails test is disabled) - #$SUDO_CMD gem uninstall rails - #$SUDO_CMD apt-get -y remove --purge nodejs -} - -# NetCore -sub_netcore(){ - echo "clean netcore" - $SUDO_CMD apt-get -y remove --purge libssl1.0-dev libkrb5-dev clang -} - -# V8 -sub_v8(){ - echo "clean v8" -} - -# NodeJS -sub_nodejs(){ - echo "clean nodejs" -} - -# SWIG -sub_swig(){ - echo "clean swig" - $SUDO_CMD apt-get -y remove --purge libpcre3-dev swig -} - -# MetaCall -sub_metacall(){ - echo "clean metacall" - $SUDO_CMD rm -rf /usr/local/share/metacall/data - $SUDO_CMD rm /usr/local/share/metacall/VERSION - $SUDO_CMD rm /usr/local/share/metacall/README.md - $SUDO_CMD rm /usr/local/share/metacall/metacall-config.cmake -} - -# Pack -sub_pack(){ - echo "clean pack" - $SUDO_CMD apt-get -y remove --purge rpm - - # Uninstall Python Port Dependencies (TODO: This must be removed when enabled the pip3 install metacall) - if [ $CLEAR_PYTHON = 1 ]; then - /usr/bin/yes | $SUDO_CMD pip3 uninstall setuptools - fi -} - -# Coverage -sub_coverage(){ - echo "clean pack" - $SUDO_CMD apt-get -y remove --purge lcov -} - -# Clear -sub_clear(){ - if [ $RUN_AS_ROOT = 1 ]; then - SUDO_CMD="" - fi - if [ $CLEAR_RAPIDJSON = 1 ]; then - sub_rapidjson - fi - if [ $CLEAR_FUNCHOOK = 1 ]; then - sub_funchook - fi - if [ $CLEAR_PYTHON = 1 ]; then - sub_python - fi - if [ $CLEAR_RUBY = 1 ]; then - sub_ruby - fi - if [ $CLEAR_NETCORE = 1 ]; then - sub_netcore - fi - if [ $CLEAR_V8 = 1 ]; then - sub_v8 - fi - if [ $CLEAR_NODEJS = 1 ]; then - sub_nodejs - fi - if [ $CLEAR_SWIG = 1 ]; then - sub_swig - fi - if [ $CLEAR_PACK = 1 ]; then - sub_pack - fi - if [ $CLEAR_COVERAGE = 1 ]; then - sub_coverage - fi - - # Clear aptitude (must be at the end) - if [ $CLEAR_APT = 1 ]; then - sub_apt - fi - - sub_metacall - - # Delete MetaCall path - rm -rf $METACALL_PATH - - echo "clean finished in workspace $ROOT_DIR" -} - -# Configuration -sub_options(){ - for var in "$@" - do - if [ "$var" = 'root' ]; then - echo "running as root" - RUN_AS_ROOT=1 - fi - if [ "$var" = 'base' ]; then - echo "apt selected" - CLEAR_APT=1 - fi - if [ "$var" = 'rapidjson' ]; then - echo "rapidjson selected" - CLEAR_RAPIDJSON=1 - fi - if [ "$var" = 'funchook' ]; then - echo "funchook selected" - CLEAR_FUNCHOOK=1 - fi - if [ "$var" = 'python' ]; then - echo "python selected" - CLEAR_PYTHON=1 - fi - if [ "$var" = 'ruby' ]; then - echo "ruby selected" - CLEAR_RUBY=1 - fi - if [ "$var" = 'netcore' ]; then - echo "netcore selected" - CLEAR_NETCORE=1 - fi - if [ "$var" = 'v8' ]; then - echo "v8 selected" - CLEAR_V8=1 - fi - if [ "$var" = 'nodejs' ]; then - echo "nodejs selected" - CLEAR_NODEJS=1 - fi - if [ "$var" = 'swig' ]; then - echo "swig selected" - CLEAR_SWIG=1 - fi - if [ "$var" = 'pack' ]; then - echo "pack selected" - CLEAR_PACK=1 - fi - if [ "$var" = 'coverage' ]; then - echo "coverage selected" - CLEAR_COVERAGE=1 - fi - done -} - -# Help -sub_help() { - echo "Usage: `basename "$0"` list of component" - echo "Components:" - echo " root" - echo " base" - echo " rapidjson" - echo " funchook" - echo " python" - echo " ruby" - echo " netcore" - echo " v8" - echo " nodejs" - echo " swig" - echo " pack" - echo " coverage" - echo "" -} - -case "$#" in - 0) - sub_help - ;; - *) - sub_options $@ - sub_clear - ;; -esac diff --git a/tools/metacall-configure.ps1 b/tools/metacall-configure.ps1 new file mode 100755 index 0000000000..ea3928e666 --- /dev/null +++ b/tools/metacall-configure.ps1 @@ -0,0 +1,477 @@ +<# +# MetaCall Build PowerShell Script by Parra Studios +# Build and install powershell script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#> + +$Global:ROOT_DIR = "$(pwd)" + +$Global:BUILD_TYPE = 'Release' +$Global:BUILD_PYTHON = 0 +$Global:BUILD_RUBY = 0 +$Global:BUILD_NETCORE = 0 +$Global:BUILD_NETCORE2 = 0 +$Global:BUILD_NETCORE5 = 0 +$Global:BUILD_V8 = 0 +$Global:BUILD_NODEJS = 0 +$Global:BUILD_TYPESCRIPT = 0 +$Global:BUILD_RUST = 0 +$Global:BUILD_ZIG = 0 +$Global:BUILD_FILE = 0 +$Global:BUILD_RPC = 0 +$Global:BUILD_WASM = 0 +$Global:BUILD_JAVA = 0 +$Global:BUILD_C = 0 +$Global:BUILD_COBOL = 0 +$Global:BUILD_SCRIPTS = 0 +$Global:BUILD_EXAMPLES = 0 +$Global:BUILD_TESTS = 0 +$Global:BUILD_BENCHMARKS = 0 +$Global:BUILD_PORTS = 0 +$Global:BUILD_ADDRESS_SANITIZER = 0 +$Global:BUILD_THREAD_SANITIZER = 0 +$Global:BUILD_MEMORY_SANITIZER = 0 +$Global:PROGNAME = $(Get-Item $PSCommandPath).Basename + +$Global:Arguments = $args + +function sub-options { + for ($i = 0; $i -lt $Arguments.Length; $i++) { + $option = $Arguments[$i] + if ("$option" -eq 'debug') { + echo "Build all scripts in debug mode" + $Global:BUILD_TYPE = 'Debug' + } + if ("$option" -eq 'release') { + echo "Build all scripts in release mode" + $Global:BUILD_TYPE = 'Release' + } + if ("$option" -eq 'relwithdebinfo') { + echo "Build all scripts in release mode with debug symbols" + $Global:BUILD_TYPE = 'RelWithDebInfo' + } + if ("$option" -eq 'python') { + echo "Build with python support" + $Global:BUILD_PYTHON = 1 + } + if ("$option" -eq 'ruby') { + echo "Build with ruby support" + $Global:BUILD_RUBY = 1 + } + if ("$option" -eq 'netcore') { + echo "Build with netcore support" + $Global:BUILD_NETCORE = 1 + } + if ("$option" -eq 'netcore2') { + echo "Build with netcore 2 support" + $Global:BUILD_NETCORE2 = 1 + } + if ("$option" -eq 'netcore5') { + echo "Build with netcore 5 support" + $Global:BUILD_NETCORE5 = 1 + } + if ("$option" -eq 'v8') { + echo "Build with v8 support" + $Global:BUILD_V8 = 1 + } + if ("$option" -eq 'nodejs') { + echo "Build with nodejs support" + $Global:BUILD_NODEJS = 1 + } + if ("$option" -eq 'typescript') { + echo "Build with typescript support" + $Global:BUILD_TYPESCRIPT = 1 + } + if ("$option" -eq 'rust') { + echo "Build with rust support" + $Global:BUILD_RUST = 1 + } + if ("$option" -eq 'zig') { + echo "Build with zig support" + $Global:BUILD_ZIG = 1 + } + if ("$option" -eq 'file') { + echo "Build with file support" + $Global:BUILD_FILE = 1 + } + if ("$option" -eq 'rpc') { + echo "Build with rpc support" + $Global:BUILD_RPC = 1 + } + if ("$option" -eq 'wasm') { + echo "Build with wasm support" + $Global:BUILD_WASM = 1 + } + if ("$option" -eq 'java') { + echo "Build with java support" + $Global:BUILD_JAVA = 1 + } + if ("$option" -eq 'c') { + echo "Build with c support" + $Global:BUILD_C = 1 + } + if ("$option" -eq 'cobol') { + echo "Build with cobol support" + $Global:BUILD_COBOL = 1 + } + if ("$option" -eq 'scripts') { + echo "Build all scripts" + $Global:BUILD_SCRIPTS = 1 + } + if ("$option" -eq 'examples') { + echo "Build all examples" + $Global:BUILD_EXAMPLES = 1 + } + if ("$option" -eq 'tests') { + echo "Build all tests" + $Global:BUILD_TESTS = 1 + } + if ("$option" -eq 'benchmarks') { + echo "Build all benchmarks" + $Global:BUILD_BENCHMARKS = 1 + } + if ("$option" -eq 'ports') { + echo "Build all ports" + $Global:BUILD_PORTS = 1 + } + if ("$option" -eq 'address-sanitizer') { + echo "Build with address sanitizers" + $Global:BUILD_ADDRESS_SANITIZER = 1 + } + if ("$option" -eq 'thread-sanitizer') { + echo "Build with thread sanitizers" + $Global:BUILD_THREAD_SANITIZER = 1 + } + if ("$option" -eq 'memory-sanitizer') { + echo "Build with memory sanitizers" + $Global:BUILD_MEMORY_SANITIZER = 1 + } + } +} + +function sub-configure { + $Global:BUILD_STRING = "-DOPTION_BUILD_LOG_PRETTY=Off " ` + + "-DOPTION_BUILD_LOADERS=On " ` + + "-DOPTION_BUILD_LOADERS_MOCK=On" + + # Scripts + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS=Off" + } + + # Python + if ($BUILD_PYTHON -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_PY=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_PY=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_PY=On" + } + } + + # Ruby + if ($BUILD_RUBY -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_RB=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RB=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_RB=On" + } + } + + # NetCore + if ($BUILD_NETCORE -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING " ` + + "-DOPTION_BUILD_LOADERS_CS=On " ` + + "-DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/1.1.10/" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + } + } + + # NetCore 2 + if ($BUILD_NETCORE2 -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING " ` + + "-DOPTION_BUILD_LOADERS_CS=On " ` + + "-DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + } + } + + # NetCore 5 + if ($BUILD_NETCORE5 -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING " ` + + "-DOPTION_BUILD_LOADERS_CS=On " ` + + "-DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/5.0.12/" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + } + } + + # V8 + if ($BUILD_V8 -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_JS=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_JS=On" + } + } + + # NodeJS + if ($BUILD_NODEJS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_NODE=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_NODE=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_NODE=On" + } + } + + # TypeScript + if ($BUILD_TYPESCRIPT -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_TS=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_TS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_TS=On" + } + } + + # Rust + if ($BUILD_RUST -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_RS=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RS=On" + } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_RS=On" + } + } + + # Zig + if ($BUILD_ZIG -eq 1) { + # TODO + # $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_ZIG=On" + + # if ($BUILD_SCRIPTS -eq 1) { + # $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_ZIG=On" + # } + + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS_ZIG=On" + } + } + + # File + if ($BUILD_FILE -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_FILE=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_FILE=On" + } + } + + # RPC + if ($BUILD_RPC -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_RPC=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RPC=On" + } + } + + # WebAssembly + if ($BUILD_WASM -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_WASM=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_WASM=On" + } + } + + # Java + if ($BUILD_JAVA -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_JAVA=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JAVA=On" + } + } + + # C + if ($BUILD_C -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_C=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_C=On" + } + } + + # Cobol + if ($BUILD_COBOL -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_LOADERS_COB=On" + + if ($BUILD_SCRIPTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_SCRIPTS_COB=On" + } + } + + # Examples + if ($BUILD_EXAMPLES -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_EXAMPLES=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_EXAMPLES=Off" + } + + # Tests + if ($BUILD_TESTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_TESTS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_TESTS=Off" + } + + # Benchmarks + if ($BUILD_BENCHMARKS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_BENCHMARKS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_BENCHMARKS=Off" + } + + # Ports + if ($BUILD_PORTS -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_PORTS=Off" + } + + # Address Sanitizer + if ($BUILD_ADDRESS_SANITIZER -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=Off" + } + + # Thread Sanitizer + if ($BUILD_THREAD_SANITIZER -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=Off" + } + + # Memory Sanitizer + if ($BUILD_MEMORY_SANITIZER -eq 1) { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=On" + } else { + $Global:BUILD_STRING = "$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=Off" + } + + # Build type + $Global:BUILD_STRING = "$BUILD_STRING -DCMAKE_BUILD_TYPE=$BUILD_TYPE" + + # Other Environment Options + $EnvOpts = Get-Content "$ROOT_DIR\CMakeConfig.txt" + + foreach ($opt in $EnvOpts.Split([System.Environment]::NewLine)) { + $Global:BUILD_STRING = "$BUILD_STRING $opt" + } + + # Execute CMake + $CustomFlags = '-DOPTION_BUILD_SECURITY=OFF -DOPTION_FORK_SAFE=OFF -DWARNINGS_ENABLED=OFF' # TODO: Enable warnings when all tests pass + echo "BUILD COMMAND: cmake $CustomFlags $BUILD_STRING .." + cmd.exe /c "cmake $CustomFlags $BUILD_STRING .." + + Exit $LASTEXITCODE +} + +function sub-help { + echo "Usage: $PROGNAME list of options" + echo "Options:" + echo " debug | release | relwithdebinfo: build type" + echo " python: build with python support" + echo " ruby: build with ruby support" + echo " netcore: build with netcore support" + echo " netcore2: build with netcore 2 support" + echo " netcore5: build with netcore 5 support" + echo " v8: build with v8 support" + echo " nodejs: build with nodejs support" + echo " typescript: build with typescript support" + echo " rust: build with rust support" + echo " zig: build with zig support" + echo " file: build with file support" + echo " rpc: build with rpc support" + echo " wasm: build with wasm support" + echo " java: build with java support" + echo " c: build with c support" + echo " cobol: build with cobol support" + echo " scripts: build all scripts" + echo " examples: build all examples" + echo " tests: build and run all tests" + echo " benchmarks: build and run all benchmarks" + echo " install: install all libraries" + echo " static: build as static libraries" + echo " ports: build all ports" + echo " address-sanitizer: build with address sanitizer" + echo " thread-sanitizer: build with thread sanitizer" + echo " memory-sanitizer: build with memory sanitizer" + echo "" +} + +switch($args.length) { + 0 { + sub-help + break + } + Default { + sub-options + sub-configure + } +} diff --git a/tools/metacall-configure.sh b/tools/metacall-configure.sh index 6a7696dbb2..7847a0d347 100755 --- a/tools/metacall-configure.sh +++ b/tools/metacall-configure.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. +# MetaCall Build Shell Script by Parra Studios +# Build and install shell script utility for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,28 +19,64 @@ # limitations under the License. # -RUN_AS_ROOT=0 -SUDO_CMD=sudo +set -euxo + +ROOT_DIR=$(pwd) BUILD_TYPE=Release BUILD_PYTHON=0 BUILD_RUBY=0 BUILD_NETCORE=0 +BUILD_NETCORE2=0 +BUILD_NETCORE5=0 +BUILD_NETCORE7=0 +BUILD_NETCORE8=0 BUILD_V8=0 BUILD_NODEJS=0 +BUILD_TYPESCRIPT=0 +BUILD_FILE=0 +BUILD_RPC=0 +BUILD_WASM=0 +BUILD_JAVA=0 +BUILD_C=0 +BUILD_COBOL=0 +BUILD_GO=0 +BUILD_RUST=0 +BUILD_ZIG=0 BUILD_SCRIPTS=0 BUILD_EXAMPLES=0 -BUILD_DISTRIBUTABLE=0 +BUILD_TESTS=0 +BUILD_BENCHMARKS=0 BUILD_PORTS=0 +BUILD_SANDBOX=0 BUILD_COVERAGE=0 +BUILD_ADDRESS_SANITIZER=0 +BUILD_THREAD_SANITIZER=0 +BUILD_MEMORY_SANITIZER=0 + +# Operative System detection +case "$(uname -s)" in + Linux*) OPERATIVE_SYSTEM=Linux;; + Darwin*) OPERATIVE_SYSTEM=Darwin;; + CYGWIN*) OPERATIVE_SYSTEM=Cygwin;; + MINGW*) OPERATIVE_SYSTEM=MinGW;; + *) OPERATIVE_SYSTEM="Unknown" +esac + +# Linux Distro detection +if [ -f /etc/os-release ]; then # Either Debian or Ubuntu + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_DISTRO=$(cat /etc/os-release | grep "^ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_VERSION_ID=$(cat /etc/os-release | grep "^VERSION_ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') +else + # TODO: Implement more distros or better detection + LINUX_DISTRO=unknown + LINUX_VERSION_ID=unknown +fi sub_options() { for option in "$@" do - if [ "$option" = 'root' ]; then - echo "Running build script as root" - RUN_AS_ROOT=1 - SUDO_CMD="" - fi if [ "$option" = 'debug' ]; then echo "Build all scripts in debug mode" BUILD_TYPE=Debug @@ -65,6 +101,22 @@ sub_options() { echo "Build with netcore support" BUILD_NETCORE=1 fi + if [ "$option" = 'netcore2' ]; then + echo "Build with netcore 2 support" + BUILD_NETCORE2=1 + fi + if [ "$option" = 'netcore5' ]; then + echo "Build with netcore 5 support" + BUILD_NETCORE5=1 + fi + if [ "$option" = 'netcore7' ]; then + echo "Build with netcore 7 support" + BUILD_NETCORE7=1 + fi + if [ "$option" = 'netcore8' ]; then + echo "Build with netcore 8 support" + BUILD_NETCORE8=1 + fi if [ "$option" = 'v8' ]; then echo "Build with v8 support" BUILD_V8=1 @@ -73,6 +125,46 @@ sub_options() { echo "Build with nodejs support" BUILD_NODEJS=1 fi + if [ "$option" = 'typescript' ]; then + echo "Build with typescript support" + BUILD_TYPESCRIPT=1 + fi + if [ "$option" = 'file' ]; then + echo "Build with file support" + BUILD_FILE=1 + fi + if [ "$option" = 'rpc' ]; then + echo "Build with rpc support" + BUILD_RPC=1 + fi + if [ "$option" = 'wasm' ]; then + echo "Build with wasm support" + BUILD_WASM=1 + fi + if [ "$option" = 'java' ]; then + echo "Build with java support" + BUILD_JAVA=1 + fi + if [ "$option" = 'c' ]; then + echo "Build with c support" + BUILD_C=1 + fi + if [ "$option" = 'cobol' ]; then + echo "Build with cobol support" + BUILD_COBOL=1 + fi + if [ "$option" = 'go' ]; then + echo "Build with go support" + BUILD_GO=1 + fi + if [ "$option" = 'rust' ]; then + echo "Build with rust support" + BUILD_RUST=1 + fi + if [ "$option" = 'zig' ]; then + echo "Build with zig support" + BUILD_ZIG=1 + fi if [ "$option" = 'scripts' ]; then echo "Build all scripts" BUILD_SCRIPTS=1 @@ -81,25 +173,57 @@ sub_options() { echo "Build all examples" BUILD_EXAMPLES=1 fi - if [ "$option" = 'distributable' ]; then - echo "Build distributable libraries" - BUILD_DISTRIBUTABLE=1 + if [ "$option" = 'tests' ]; then + echo "Build all tests" + BUILD_TESTS=1 + fi + if [ "$option" = 'benchmarks' ]; then + echo "Build all benchmarks" + BUILD_BENCHMARKS=1 fi if [ "$option" = 'ports' ]; then echo "Build all ports" BUILD_PORTS=1 fi + if [ "$option" = 'sandbox' ]; then + echo "Build with sandboxing support" + BUILD_SANDBOX=1 + fi if [ "$option" = 'coverage' ]; then echo "Build all coverage reports" BUILD_COVERAGE=1 fi + if [ "$option" = 'address-sanitizer' ]; then + echo "Build with address sanitizer" + BUILD_ADDRESS_SANITIZER=1 + fi + if [ "$option" = 'thread-sanitizer' ]; then + echo "Build with thread sanitizer" + BUILD_THREAD_SANITIZER=1 + fi + if [ "$option" = 'memory-sanitizer' ]; then + echo "Build with memory sanitizer" + BUILD_MEMORY_SANITIZER=1 + fi done } +sub_find_dotnet_runtime() { + NETCORE_BASE_PATH=`dotnet --list-runtimes | grep -m 1 "Microsoft.NETCore.App $1"` + echo "`echo \"$NETCORE_BASE_PATH\" | awk '{ print $3 }' | tail -c +2 | head -c -2`/`echo \"$NETCORE_BASE_PATH\" | awk '{ print $2 }'`/" +} + sub_configure() { - BUILD_STRING="-DOPTION_BUILD_LOADERS=On \ + BUILD_STRING="-DOPTION_BUILD_LOG_PRETTY=Off \ + -DOPTION_BUILD_LOADERS=On \ -DOPTION_BUILD_LOADERS_MOCK=On" + + # Enable build with musl libc + if [ "$LINUX_DISTRO" = "alpine" ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_MUSL=On" + fi + # Scripts if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS=On" @@ -109,13 +233,21 @@ sub_configure() { # Python if [ $BUILD_PYTHON = 1 ]; then - BUILD_STRING="$BUILD_STRING \ - -DPYTHON_EXECUTABLE=/usr/bin/python3.5 \ - -DOPTION_BUILD_LOADERS_PY=On" + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_PY=On" + + # Patch for Darwin Python headers + if [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + BUILD_STRING="$BUILD_STRING -DPython3_INCLUDE_DIR=$(python3 -c "import sysconfig; print(sysconfig.get_path('include'))")" + # BUILD_STRING="$BUILD_STRING -DPython3_LIBRARY=$(python3 -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")" + fi if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_PY=On" fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_PY=On" + fi fi # Ruby @@ -125,17 +257,85 @@ sub_configure() { if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RB=On" fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_RB=On" + fi fi # NetCore if [ $BUILD_NETCORE = 1 ]; then BUILD_STRING="$BUILD_STRING \ -DOPTION_BUILD_LOADERS_CS=On \ - -DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/1.1.10/" + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 1`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 2 + if [ $BUILD_NETCORE2 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 2`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 5 + if [ $BUILD_NETCORE5 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 5`" if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 7 + if [ $BUILD_NETCORE7 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 7`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi + fi + + # NetCore 8 + if [ $BUILD_NETCORE8 = 1 ]; then + BUILD_STRING="$BUILD_STRING \ + -DOPTION_BUILD_LOADERS_CS=On \ + -DDOTNET_CORE_PATH=`sub_find_dotnet_runtime 8`" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_CS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_CS=On" + fi fi # V8 @@ -145,6 +345,10 @@ sub_configure() { if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JS=On" fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_JS=On" + fi fi # NodeJS @@ -154,6 +358,122 @@ sub_configure() { if [ $BUILD_SCRIPTS = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_NODE=On" fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_NODE=On" + fi + fi + + # TypeScript + if [ $BUILD_TYPESCRIPT = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_TS=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_TS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_TS=On" + fi + fi + + # File + if [ $BUILD_FILE = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_FILE=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_FILE=On" + fi + fi + + # RPC + if [ $BUILD_RPC = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_RPC=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RPC=On" + fi + fi + + # WebAssembly + if [ $BUILD_WASM = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_WASM=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_WASM=On" + fi + fi + + # Java + if [ $BUILD_JAVA = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_JAVA=On" + + if [ "$LINUX_DISTRO" = "alpine" ]; then + BUILD_STRING="$BUILD_STRING -DJAVA_HOME=/usr/lib/jvm/java-1.8-openjdk" + fi + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_JAVA=On" + fi + fi + + # C + if [ $BUILD_C = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_C=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_C=On" + fi + fi + + # Cobol + if [ $BUILD_COBOL = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_COB=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_COB=On" + fi + fi + + # Go + if [ $BUILD_GO = 1 ]; then + # TODO + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_GO=On" + + # if [ $BUILD_SCRIPTS = 1 ]; then + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_GO=On" + # fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_GO=On" + fi + fi + + # Rust + if [ $BUILD_RUST = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_RS=On" + + if [ $BUILD_SCRIPTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_RS=On" + fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_RS=On" + fi + fi + + # Zig + if [ $BUILD_ZIG = 1 ]; then + # TODO + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_LOADERS_ZIG=On" + + # if [ $BUILD_SCRIPTS = 1 ]; then + # BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_SCRIPTS_ZIG=On" + # fi + + if [ $BUILD_PORTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS_ZIG=On" + fi fi # Examples @@ -163,11 +483,18 @@ sub_configure() { BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_EXAMPLES=Off" fi - # Distributable - if [ $BUILD_DISTRIBUTABLE = 1 ]; then - BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_DIST_LIBS=On" + # Tests + if [ $BUILD_TESTS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_TESTS=On" else - BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_DIST_LIBS=Off" + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_TESTS=Off" + fi + + # Benchmarks + if [ $BUILD_BENCHMARKS = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_BENCHMARKS=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_BENCHMARKS=Off" fi # Ports @@ -177,6 +504,13 @@ sub_configure() { BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PORTS=Off" fi + # Sandbox + if [ $BUILD_SANDBOX = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PLUGINS_SANDBOX=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_PLUGINS_SANDBOX=Off" + fi + # Coverage if [ $BUILD_COVERAGE = 1 ]; then BUILD_STRING="$BUILD_STRING -DOPTION_COVERAGE=On" @@ -184,32 +518,78 @@ sub_configure() { BUILD_STRING="$BUILD_STRING -DOPTION_COVERAGE=Off" fi + # Address Sanitizer + if [ $BUILD_ADDRESS_SANITIZER = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_ADDRESS_SANITIZER=Off" + fi + + # Thread Sanitizer + if [ $BUILD_THREAD_SANITIZER = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_THREAD_SANITIZER=Off" + fi + + # Memory Sanitizer + if [ $BUILD_MEMORY_SANITIZER = 1 ]; then + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=On" + else + BUILD_STRING="$BUILD_STRING -DOPTION_BUILD_MEMORY_SANITIZER=Off" + fi + + # Split cmake config file line by line and add each line to the build string + CMAKE_CONFIG_FILE="$ROOT_DIR/CMakeConfig.txt" + if [ -f $CMAKE_CONFIG_FILE ]; then + while IFS= read -r line + do + BUILD_STRING="$BUILD_STRING $line" + done < "$CMAKE_CONFIG_FILE" + fi + # Build type BUILD_STRING="$BUILD_STRING -DCMAKE_BUILD_TYPE=$BUILD_TYPE" - + # Execute CMake - cmake $BUILD_STRING .. + cmake -Wno-dev -DOPTION_GIT_HOOKS=Off $BUILD_STRING .. } sub_help() { echo "Usage: `basename "$0"` list of options" echo "Options:" - echo " root: build being run by root" echo " debug | release | relwithdebinfo: build type" echo " python: build with python support" echo " ruby: build with ruby support" echo " netcore: build with netcore support" + echo " netcore2: build with netcore 2 support" + echo " netcore5: build with netcore 5 support" + echo " netcore7: build with netcore 7 support" + echo " netcore8: build with netcore 8 support" echo " v8: build with v8 support" echo " nodejs: build with nodejs support" + echo " typescript: build with typescript support" + echo " file: build with file support" + echo " rpc: build with rpc support" + echo " wasm: build with wasm support" + echo " java: build with java support" + echo " c: build with c support" + echo " cobol: build with cobol support" + echo " go: build with go support" + echo " rust: build with rust support" + echo " zig: build with zig support" echo " scripts: build all scripts" echo " examples: build all examples" - echo " distributable: build distributable libraries" echo " tests: build and run all tests" + echo " benchmarks: build and run all benchmarks" echo " install: install all libraries" echo " static: build as static libraries" - echo " dynamic: build as dynamic libraries" echo " ports: build all ports" + echo " sandbox: build with sandboxing support" echo " coverage: build all coverage reports" + echo " address-sanitizer: build with address sanitizer" + echo " thread-sanitizer: build with thread sanitizer" + echo " memory-sanitizer: build with memory sanitizer" echo "" } diff --git a/tools/metacall-environment.ps1 b/tools/metacall-environment.ps1 new file mode 100755 index 0000000000..486edf7713 --- /dev/null +++ b/tools/metacall-environment.ps1 @@ -0,0 +1,412 @@ +<# +# MetaCall Build PowerShell Script by Parra Studios +# Build and install powershell script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#> + +$PSDefaultParameterValues['*:Encoding'] = 'utf8' + +$Global:ROOT_DIR = "$(Get-Location)" +$Global:SHOW_HELP = 0 +$Global:PROGNAME = $(Get-Item $PSCommandPath).Basename +$Global:Arguments = $args + +function Set-Python { + Write-Output "Install Python" + Set-Location $ROOT_DIR + + $PythonVersion = '3.9.7' + $RuntimeDir = "$env:ProgramFiles\Python3" + $DepsDir = "$ROOT_DIR\dependencies" + + mkdir -Force $DepsDir + mkdir -Force $RuntimeDir + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\python_installer.exe")) { + # Download installer + Write-Output "Python installer not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://www.python.org/ftp/python/$PythonVersion/python-$PythonVersion-amd64.exe", "$(Get-Location)\python_installer.exe") + } + + Write-Output "Installing python $PythonVersion" + + # Install Python + ./python_installer.exe /quiet "TargetDir=$RuntimeDir" Include_debug=1 Include_symbols=1 PrependPath=1 CompileAll=1 + + # Set environment variables + Add-to-Path $RuntimeDir + Add-to-Path "$RuntimeDir\Scripts" + [Environment]::SetEnvironmentVariable("PIP_TARGET", "$RuntimeDir\Lib") + + # No patch, save vars for later use + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + $PythonRuntimeDir = $RuntimeDir.Replace('\', '/') + + Write-Output "-DPython3_VERSION=$PythonVersion" >> $EnvOpts + Write-Output "-DPython3_ROOT_DIR=""$PythonRuntimeDir""" >> $EnvOpts + Write-Output "-DPython3_EXECUTABLE=""$PythonRuntimeDir/python.exe""" >> $EnvOpts + Write-Output "-DPython3_INCLUDE_DIRS=""$PythonRuntimeDir/include""" >> $EnvOpts + Write-Output "-DPython3_LIBRARIES=""$PythonRuntimeDir/libs/python39_d.lib;$PythonRuntimeDir/libs/python39.lib""" >> $EnvOpts + Write-Output "-DPython3_Development_FOUND=1" >> $EnvOpts + Write-Output "-DPython3_FIND_REGISTRY=NEVER" >> $EnvOpts + + # Install dependencies for tests + pip3 install requests + pip3 install setuptools + pip3 install wheel + pip3 install rsa + pip3 install scipy + pip3 install numpy + pip3 install scikit-learn + pip3 install joblib +} + +function Set-Nodejs { + Write-Output "Install Node.js" + Set-Location $ROOT_DIR + + $DepsDir = "$ROOT_DIR\dependencies" + $NodeVersion = "14.18.2" + $DLLReleaseVer = "v0.0.1" + $RuntimeDir = "$env:ProgramFiles\nodejs" + + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\node.msi")) { + # Download installer + Write-Output "Nodejs MSI installer not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://nodejs.org/download/release/v$NodeVersion/node-v$NodeVersion-x64.msi", "$DepsDir\node.msi") + } + + if (!(Test-Path -Path "$DepsDir\node_headers.tar.gz")) { + # Download installer + Write-Output "Nodejs headers installer not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://nodejs.org/download/release/v$NodeVersion/node-v$NodeVersion-headers.tar.gz", "$DepsDir\node_headers.tar.gz") + } + + msiexec.exe /quiet /i "$DepsDir\node.msi" + + Add-to-Path $RuntimeDir + Add-to-Path "$RuntimeDir\include" + + cmake -E tar xzf node_headers.tar.gz + + mkdir "$RuntimeDir\include" + Robocopy.exe /move /e "$DepsDir\node-v$NodeVersion\include" "$RuntimeDir\include" /NFL /NDL /NJH /NJS /NC /NS /NP + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\node_dll.zip")) { + # Download installer + Write-Output "Nodejs Custom DLLs not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://github.com/metacall/node.dll/releases/download/$DLLReleaseVer/node-shared-v$NodeVersion-x64.zip", "$DepsDir\node_dll.zip") + } + + Expand-Archive -Path "node_dll.zip" -DestinationPath "$RuntimeDir\lib" + + $NodeDir = $RuntimeDir.Replace('\', '/') + + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + Write-Output "-DNodeJS_VERSION=""$NodeVersion""" >> $EnvOpts + Write-Output "-DNodeJS_INCLUDE_DIRS=""$NodeDir/include/node""" >> $EnvOpts + Write-Output "-DNodeJS_LIBRARY=""$NodeDir/lib/libnode.lib""" >> $EnvOpts + Write-Output "-DNodeJS_EXECUTABLE=""$NodeDir/node.exe""" >> $EnvOpts + Write-Output "-DNodeJS_LIBRARY_NAME=""libnode.dll""" >> $EnvOpts + Write-Output "-DNodeJS_LIBRARY_NAME_PATH=""$NodeDir/lib/libnode.dll""" >> $EnvOpts + + if ($Arguments -contains "c") { + # Required for test source/tests/metacall_node_port_c_lib_test + if (!(Test-Path -Path "$DepsDir\libgit2")) { + # Clone libgit2 + git clone --depth 1 --branch v1.8.4 https://github.com/libgit2/libgit2 + } + + $InstallDir = "$DepsDir\libgit2\build\dist" + + mkdir "$DepsDir\libgit2\build" + mkdir "$InstallDir" + Set-Location "$DepsDir\libgit2\build" + + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=OFF -DBUILD_CLI=OFF .. + cmake --build . "-j$((Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors)" + cmake --install . --prefix "$InstallDir" + + Write-Output "-DLibGit2_LIBRARY=""$InstallDir\lib\git2.lib""" >> $EnvOpts + Write-Output "-DLibGit2_INCLUDE_DIR=""$InstallDir\include""" >> $EnvOpts + } +} + +function Set-Java { + Write-Output "Install Java" + $JAVA_VERSION = "17.0.5" + $RuntimeDir = "$env:ProgramFiles\openjdk" + $DepsDir = "$ROOT_DIR\dependencies" + + Set-Location $DepsDir + + if (!(Test-Path -Path "$DepsDir\openjdk.zip")) { + # Download installer + Write-Output "OpenJDK not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://aka.ms/download-jdk/microsoft-jdk-$JAVA_VERSION-windows-x64.zip", "$DepsDir\openjdk.zip") + } + + Expand-Archive -Path "openjdk.zip" -DestinationPath "$RuntimeDir" + robocopy /move /e "$RuntimeDir\jdk-$JAVA_VERSION+8" "$RuntimeDir" /NFL /NDL /NJH /NJS /NC /NS /NP + + Add-to-Path "JAVA_HOME=$RuntimeDir" + Add-to-Path "$RuntimeDir\bin" + Add-to-Path "$RuntimeDir\bin\server" +} + +function Set-Ruby { + Write-Output "Install Ruby" + + Set-Location $ROOT_DIR + $RuntimeDir = "$env:ProgramFiles\ruby" + $DepsDir = "$ROOT_DIR\dependencies" + + if (!(Test-Path -Path "$DepsDir\ruby-mswin.7z")) { + # Download installer + Write-Output "Ruby not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://github.com/metacall/ruby-loco/releases/download/ruby-master/ruby-mswin.7z", "$DepsDir\ruby-mswin.7z") + } + + mkdir "$DepsDir\ruby-mswin" + 7z x "$DepsDir\ruby-mswin.7z" -o"$DepsDir" + + robocopy /move /e "$DepsDir\ruby-mswin\" $RuntimeDir + + Add-to-Path "$RuntimeDir\bin" + + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + $RubyDir = $RuntimeDir.Replace('\', '/') + + Write-Output "-DRuby_VERSION_STRING=""3.5.0""" >> $EnvOpts + Write-Output "-DRuby_INCLUDE_DIR=""$RubyDir/include/ruby-3.5.0+0""" >> $EnvOpts + Write-Output "-DRuby_EXECUTABLE=""$RubyDir/bin/ruby.exe""" >> $EnvOpts + Write-Output "-DRuby_LIBRARY=""$RubyDir/lib/x64-vcruntime140-ruby350.lib""" >> $EnvOpts + Write-Output "-DRuby_LIBRARY_NAME=""$RubyDir/bin/x64-vcruntime140-ruby350.dll""" >> $EnvOpts + Write-Output "-DRuby_LIBRARY_SEARCH_PATHS=""$RubyDir/bin/ruby_builtin_dlls""" >> $EnvOpts +} + +function Set-TypeScript { + Write-Output "Install TypeScript" + npm i react@latest -g + npm i react-dom@latest -g +} + +function Set-Curl { + Write-Output "Installing cURL" + + Set-Location $ROOT_DIR + $RuntimeDir = "$env:ProgramFiles\curl" + $DepsDir = "$ROOT_DIR\dependencies" + $Version = "8.3.0_2" + + if (!(Test-Path -Path "$DepsDir\curl.zip")) { + # Download installer + Write-Output "Curl not found downloading now..." + (New-Object Net.WebClient).DownloadFile("https://curl.se/windows/dl-$Version/curl-$Version-win64-mingw.zip", "$DepsDir\curl.zip") + } + + Set-Location $DepsDir + + 7z x "$DepsDir\curl.zip" + + robocopy /move /e "$DepsDir\curl-$Version-win64-mingw" $RuntimeDir + + Add-to-Path "$RuntimeDir\bin" + + $EnvOpts = "$ROOT_DIR\build\CMakeConfig.txt" + $CurlDir = $RuntimeDir.Replace('\', '/') + + $CURL_INCLUDE_DIR="$CurlDir/include" + $CURL_LIB="$CurlDir/lib/libcurl.dll.a" + $CURL_LIB_NAME="$CurlDir/bin/libcurl-x64.dll" + + Write-Output "-DCURL_INCLUDE_DIR=""$CURL_INCLUDE_DIR""" >> $EnvOpts + Write-Output "-DCURL_INCLUDE_DIRS=""$CURL_INCLUDE_DIR""" >> $EnvOpts + Write-Output "-DCURL_LIBRARY=""$CURL_LIB""" >> $EnvOpts + Write-Output "-DCURL_LIBRARY_NAME=""$CURL_LIB_NAME""" >> $EnvOpts +} + +function Add-to-Path { + $GivenPath = $args[0] + + $NewPath = "$GivenPath;$Env:PATH" + setx /M PATH $NewPath + $Env:PATH = $NewPath + $GivenPath >> $env:GITHUB_PATH + + if ($Null -ne $Env:GITHUB_ENV) { + Write-Output "PATH=$Env:PATH" >> $Env:GITHUB_ENV + } + + refreshenv + + Write-Output "PATH:: " $Env:PATH +} + +function Set-Base { + $DepsDir = "$ROOT_DIR\dependencies" + + # Check if 7zip is installed + if (!(Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | ?{$_.DisplayName -like "7-Zip*"})) { + Write-Output "Install 7zip" + + if (!(Test-Path -Path "$DepsDir\7zip.exe")) { + # Download installer + (New-Object Net.WebClient).DownloadFile("https://www.7-zip.org/a/7z2201-x64.exe", "$DepsDir\7zip.exe") + } + + # https://gist.github.com/dansmith65/7dd950f183af5f5deaf9650f2ad3226c + $installerPath = "$DepsDir\7zip.exe" + Start-Process -FilePath $installerPath -Args "/S" -Verb RunAs -Wait + Add-to-Path "$env:ProgramFiles\7-Zip" + } +} + +# Configure +function Configure { + # Create option variables file + mkdir "$ROOT_DIR\build" + New-Item -Path "$ROOT_DIR\build\CMakeConfig.txt" + + # Install base requirements + Set-Base + + for ($i = 0; $i -lt $Arguments.Length; $i++) { + $var = $Arguments[$i] + if ("$var" -eq 'python') { + Write-Output "python selected" + Set-Python + } + if ("$var" -eq 'ruby') { + Write-Output "ruby selected" + Set-Ruby + } + if ("$var" -eq 'netcore') { + Write-Output "netcore selected" + } + if ("$var" -eq 'netcore2') { + Write-Output "netcore 2 selected" + } + if ("$var" -eq 'netcore5') { + Write-Output "netcore 5 selected" + } + if ("$var" -eq 'rapidjson') { + Write-Output "rapidjson selected" + } + if (("$var" -eq 'v8') -or ("$var" -eq 'v8rep54')) { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep57') { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep58') { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep52') { + Write-Output "v8 selected" + } + if ("$var" -eq 'v8rep51') { + Write-Output "v8 selected" + } + if ("$var" -eq 'nodejs') { + Write-Output "nodejs selected" + Set-Nodejs + } + if ("$var" -eq 'typescript') { + Write-Output "typescript selected" + Set-TypeScript + } + if ("$var" -eq 'file') { + Write-Output "file selected" + } + if ("$var" -eq 'rpc') { + Write-Output "rpc selected" + Set-Curl + } + if ("$var" -eq 'wasm') { + Write-Output "wasm selected" + } + if ("$var" -eq 'java') { + Write-Output "java selected" + Set-Java + } + if ("$var" -eq 'c') { + Write-Output "c selected" + } + if ("$var" -eq 'cobol') { + Write-Output "cobol selected" + } + if ("$var" -eq 'go') { + Write-Output "go selected" + } + if ("$var" -eq 'rust') { + Write-Output "rust selected" + } + if ("$var" -eq 'metacall') { + Write-Output "metacall selected" + } + if ("$var" -eq 'pack') { + Write-Output "pack selected" + } + if ("$var" -eq 'clangformat') { + Write-Output "clangformat selected" + } + } +} + +# Help +function Help { + Write-Output "Usage: $PROGNAME list of component" + Write-Output "Components:" + Write-Output " python" + Write-Output " ruby" + Write-Output " netcore" + Write-Output " netcore2" + Write-Output " netcore5" + Write-Output " rapidjson" + Write-Output " v8" + Write-Output " v8rep51" + Write-Output " v8rep54" + Write-Output " v8rep57" + Write-Output " v8rep58" + Write-Output " nodejs" + Write-Output " typescript" + Write-Output " file" + Write-Output " rpc" + Write-Output " wasm" + Write-Output " java" + Write-Output " c" + Write-Output " cobol" + Write-Output " go" + Write-Output " metacall" + Write-Output " pack" + Write-Output " clangformat" + Write-Output "" +} + +switch($args.length) { + 0 { + Help + Break + } + Default { + Configure + } +} diff --git a/tools/metacall-environment.sh b/tools/metacall-environment.sh index bad10fa7e2..577889af99 100755 --- a/tools/metacall-environment.sh +++ b/tools/metacall-environment.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Configuration Environment Bash Script by Parra Studios +# MetaCall Configuration Environment Shell Script by Parra Studios # Configure and install MetaCall environment script utility. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,18 +19,22 @@ # limitations under the License. # +set -euxo + ROOT_DIR=$(pwd) -RUN_AS_ROOT=0 -SUDO_CMD=sudo APT_CACHE=0 APT_CACHE_CMD="" -INSTALL_APT=1 +BUILD_TYPE=Release +INSTALL_BASE=1 INSTALL_PYTHON=0 INSTALL_RUBY=0 INSTALL_RAPIDJSON=0 -INSTALL_FUNCHOOK=0 INSTALL_NETCORE=0 +INSTALL_NETCORE2=0 +INSTALL_NETCORE5=0 +INSTALL_NETCORE7=0 +INSTALL_NETCORE8=0 INSTALL_V8=0 INSTALL_V8REPO=0 INSTALL_V8REPO58=0 @@ -39,79 +43,200 @@ INSTALL_V8REPO54=0 INSTALL_V8REPO52=0 INSTALL_V8REPO51=0 INSTALL_NODEJS=0 +INSTALL_TYPESCRIPT=0 +INSTALL_FILE=0 +INSTALL_RPC=0 INSTALL_WASM=0 -INSTALL_SWIG=0 -INSTALL_METACALL=0 +INSTALL_JAVA=0 +INSTALL_C=0 +INSTALL_COBOL=0 +INSTALL_GO=0 +INSTALL_RUST=0 INSTALL_PACK=0 INSTALL_COVERAGE=0 +INSTALL_CLANGFORMAT=0 +INSTALL_BACKTRACE=0 +INSTALL_SANDBOX=0 SHOW_HELP=0 PROGNAME=$(basename $0) -# Base packages -sub_apt(){ - echo "configure apt" - cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install build-essential git cmake wget apt-utils apt-transport-https gnupg dirmngr ca-certificates -} +# Operative System detection +case "$(uname -s)" in + Linux*) OPERATIVE_SYSTEM=Linux;; + Darwin*) OPERATIVE_SYSTEM=Darwin;; + CYGWIN*) OPERATIVE_SYSTEM=Cygwin;; + MINGW*) OPERATIVE_SYSTEM=MinGW;; + *) OPERATIVE_SYSTEM="Unknown" +esac + +# Architecture detection +case "$(uname -m)" in + x86_64) ARCHITECTURE="amd64";; + arm64) ARCHITECTURE="arm64";; + *) ARCHITECTURE="Unknown";; +esac -# Swig -sub_swig(){ - echo "configure swig" +# Check out for sudo +if [ "`id -u`" = '0' ]; then + SUDO_CMD="" +else + SUDO_CMD=sudo +fi + +# Linux Distro detection +if [ -f /etc/os-release ]; then # Either Debian or Ubuntu + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_DISTRO=$(cat /etc/os-release | grep "^ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_VERSION_ID=$(cat /etc/os-release | grep "^VERSION_ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') +else + # TODO: Implement more distros or better detection + LINUX_DISTRO=unknown + LINUX_VERSION_ID=unknown +fi + +# Base packages +sub_base(){ + echo "configure base packages" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libpcre3-dev swig - # Install Python Port Dependencies (TODO: This must be transformed into pip3 install metacall) - $SUDO_CMD pip3 install setuptools + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends build-essential git cmake wget apt-utils apt-transport-https gnupg dirmngr ca-certificates + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk update + $SUDO_CMD apk add --no-cache g++ make git cmake wget gnupg ca-certificates + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install llvm cmake git wget gnupg ca-certificates + fi } # Python sub_python(){ echo "configure python" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install python3 python3-dev python3-pip - $SUDO_CMD pip3 install django - $SUDO_CMD pip3 install requests - $SUDO_CMD pip3 install 'rsa==3.4.2' - $SUDO_CMD pip3 install 'scipy==1.2.1' - $SUDO_CMD pip3 install 'numpy==1.16.1' - $SUDO_CMD pip3 install 'scikit-learn==0.19.1' - $SUDO_CMD pip3 install 'joblib==0.13.2' + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + if [ "${BUILD_TYPE}" = "Debug" ]; then + PYTHON3_PKG=python3-dbg + else + PYTHON3_PKG=python3 + fi + + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends $PYTHON3_PKG python3-dev python3-pip + + # Python test dependencies + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends \ + python3-requests \ + python3-setuptools \ + python3-wheel \ + python3-rsa \ + python3-scipy \ + python3-numpy \ + python3-sklearn \ + python3-joblib + + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + # Fix to a lower Python version (3.9) in order avoid conflicts with Python dependency of Clang from C Loader + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main python3=3.9.16-r0 python3-dev=3.9.16-r0 + + # Python test dependencies + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/community \ + py3-pip=20.3.4-r1 \ + py3-rsa=4.7.2-r0 \ + py3-scipy=1.7.2-r0 \ + py3-numpy=1.21.4-r0 \ + py3-scikit-learn=0.24.0-r1 \ + py3-joblib=1.0.1-r1 + + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main \ + py3-requests=2.26.0-r1 \ + py3-setuptools=52.0.0-r4 \ + py3-wheel=0.36.2-r2 + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install pyenv openssl + export PKG_CONFIG_PATH=$(brew --prefix openssl)/lib/pkgconfig + export PYTHON_CONFIGURE_OPTS="--enable-shared" + PYTHON_VERSION_SMALL="3.13" + PYTHON_VERSION="${PYTHON_VERSION_SMALL}.0" + pyenv install ${PYTHON_VERSION} + pyenv global ${PYTHON_VERSION} + pyenv rehash + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + ENV_FILE="$ROOT_DIR/build/.env" + + echo eval "$(pyenv init -)" >> $ENV_FILE + . $ENV_FILE + + echo "-DPython3_INCLUDE_DIRS=$HOME/.pyenv/versions/${PYTHON_VERSION}/include/python${PYTHON_VERSION_SMALL}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_LIBRARY=$HOME/.pyenv/versions/${PYTHON_VERSION}/lib/libpython${PYTHON_VERSION_SMALL}.dylib" >> $CMAKE_CONFIG_PATH + echo "-DPython3_EXECUTABLE=$HOME/.pyenv/versions/${PYTHON_VERSION}/bin/python${PYTHON_VERSION_SMALL}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_ROOT=$HOME/.pyenv/versions/${PYTHON_VERSION}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_VERSION=${PYTHON_VERSION}" >> $CMAKE_CONFIG_PATH + echo "-DPython3_FIND_FRAMEWORK=NEVER" >> $CMAKE_CONFIG_PATH + + pip3 install requests + pip3 install setuptools + pip3 install wheel + pip3 install rsa + pip3 install scipy + pip3 install numpy + pip3 install joblib + pip3 install scikit-learn + fi } # Ruby sub_ruby(){ echo "configure ruby" cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev ruby2.3-dev - # TODO: Review conflict with NodeJS (currently rails test is disabled) - #curl -sL https://deb.nodesource.com/setup_4.x | $SUDO_CMD bash - - #$SUDO_CMD apt-get -y --no-install-recommends install nodejs - #$SUDO_CMD gem install rails + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends ruby ruby-dev + + # TODO: Review conflict with NodeJS (currently rails test is disabled) + #wget https://deb.nodesource.com/setup_4.x | $SUDO_CMD bash - + #$SUDO_CMD apt-get -y --no-install-recommends install nodejs + #$SUDO_CMD gem install rails + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache ruby ruby-dev + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install ruby@3.2 + brew link ruby@3.2 --force --overwrite + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + RUBY_PREFIX="$(brew --prefix ruby@3.2)" + RUBY_VERSION="$(ruby -e 'puts RUBY_VERSION')" + echo "-DRuby_INCLUDE_DIR=$RUBY_PREFIX/include/ruby-3.2.0" >> $CMAKE_CONFIG_PATH + echo "-DRuby_LIBRARY=$RUBY_PREFIX/lib/libruby.3.2.dylib" >> $CMAKE_CONFIG_PATH + echo "-DRuby_EXECUTABLE=$RUBY_PREFIX/bin/ruby" >> $CMAKE_CONFIG_PATH + echo "-DRuby_VERSION=$RUBY_VERSION" >> $CMAKE_CONFIG_PATH + fi } # RapidJSON sub_rapidjson(){ echo "configure rapidjson" cd $ROOT_DIR - git clone https://github.com/miloyip/rapidjson.git - cd rapidjson - git checkout v1.1.0 - mkdir build - cd build - cmake -DRAPIDJSON_BUILD_DOC=Off -DRAPIDJSON_BUILD_EXAMPLES=Off -DRAPIDJSON_BUILD_TESTS=Off .. - make - $SUDO_CMD make install - cd ../.. && rm -rf ./rapidjson -} -# FuncHook -sub_funchook(){ - echo "configure funchook" - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install autoconf + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + git clone https://github.com/Tencent/rapidjson.git + cd rapidjson + git checkout 24b5e7a8b27f42fa16b96fc70aade9106cf7102f + mkdir build + cd build + cmake -DRAPIDJSON_BUILD_DOC=Off -DRAPIDJSON_BUILD_EXAMPLES=Off -DRAPIDJSON_BUILD_TESTS=Off .. + make -j$(getconf _NPROCESSORS_ONLN) + $SUDO_CMD make install + cd ../.. && rm -rf ./rapidjson + fi } # NetCore @@ -119,81 +244,187 @@ sub_netcore(){ echo "configure netcore" cd $ROOT_DIR - $SUDO_CMD apt-get update && apt-get $APT_CACHE_CMD install -y --no-install-recommends \ - libc6 libcurl3 libgcc1 libgssapi-krb5-2 libicu57 liblttng-ust0 libssl1.0.2 libstdc++6 libunwind8 libuuid1 zlib1g - - # # Install .NET Core - # DOTNET_VERSION=1.1.10 - # DOTNET_DOWNLOAD_URL=https://dotnetcli.blob.core.windows.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-debian.9-x64.$DOTNET_VERSION.tar.gz - - # wget $DOTNET_DOWNLOAD_URL -O dotnet.tar.gz - # mkdir -p /usr/share/dotnet - # tar -zxf dotnet.tar.gz -C /usr/share/dotnet - # rm dotnet.tar.gz - # ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet - - # Install .NET Runtime - DOTNET_SDK_VERSION=1.1.11 - DOTNET_SDK_DOWNLOAD_URL=https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-dev-debian.9-x64.$DOTNET_SDK_VERSION.tar.gz - - wget $DOTNET_SDK_DOWNLOAD_URL -O dotnet.tar.gz - mkdir -p /usr/share/dotnet - tar -zxf dotnet.tar.gz -C /usr/share/dotnet - rm dotnet.tar.gz - ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet - - # Trigger the population of the local package cache - mkdir warmup - cd warmup - dotnet new - cd .. - rm -rf warmup - rm -rf /tmp/NuGetScratch + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Debian Stretch + $SUDO_CMD apt-get update && $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends \ + libc6 libcurl3 libgcc1 libgssapi-krb5-2 libicu57 liblttng-ust0 libssl1.0.2 libstdc++6 libunwind8 libuuid1 zlib1g + + # Install .NET Sdk + DOTNET_SDK_VERSION=1.1.11 + DOTNET_SDK_DOWNLOAD_URL=https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-dev-debian.9-x64.$DOTNET_SDK_VERSION.tar.gz + + wget $DOTNET_SDK_DOWNLOAD_URL -O dotnet.tar.gz + mkdir -p /usr/share/dotnet + tar -zxf dotnet.tar.gz -C /usr/share/dotnet + rm dotnet.tar.gz + ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet + + # Trigger the population of the local package cache + mkdir warmup + cd warmup + dotnet new + cd .. + rm -rf warmup + rm -rf /tmp/NuGetScratch + fi + fi } -# V8 Repository -sub_v8repo(){ - echo "configure v8 from repository" +# NetCore 2 +sub_netcore2(){ + echo "configure netcore 2" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install software-properties-common - # V8 5.1 - if [ $INSTALL_V8REPO51 = 1 ]; then - $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv851.list" - $SUDO_CMD sh -c "echo \"deb http://archive.ubuntu.com/ubuntu trusty main\" > /etc/apt/sources.list.d/libicu52.list" - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --allow-unauthenticated install libicu52 libv8-5.1.117 libv8-5.1-dev + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Set up repository + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + # Install .NET Core Sdk + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-2.2 + fi fi +} + +# NetCore 5 +sub_netcore5(){ + echo "configure netcore 5" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Set up repository + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb - # V8 5.4 - if [ $INSTALL_V8REPO54 = 1 ]; then - $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-5.4/ubuntu xenial main\" > /etc/apt/sources.list.d/libv854.list" - wget http://launchpadlibrarian.net/234847357/libicu55_55.1-7_amd64.deb - $SUDO_CMD dpkg -i libicu55_55.1-7_amd64.deb - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --allow-unauthenticated install libicu55 libv8-5.4-dev - $SUDO_CMD rm libicu55_55.1-7_amd64.deb + # Install .NET Core Sdk + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-5.0 + fi fi +} + +# NetCore 7 +sub_netcore7(){ + echo "configure netcore 7" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + # Set up repository + wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + # Install .NET Core Sdk + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends apt-transport-https + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-7.0 + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release - # V8 5.2 - if [ $INSTALL_V8REPO52 = 1 ]; then - $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.2 - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libicu55 libv8-5.2-dev + case ${LINUX_DISTRO} in + debian) + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ]; then + CODENAME="unstable" + else + CODENAME="${VERSION_CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + fi + ;; + esac + + if [ "${CODENAME}" = "noble" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends software-properties-common + $SUDO_CMD add-apt-repository ppa:dotnet/backports + fi + + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends dotnet-sdk-7.0 + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache dotnet7-sdk + fi fi +} - # V8 5.7 - if [ $INSTALL_V8REPO57 = 1 ]; then - $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.7 - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libicu55 libv8-5.7-dev +# NetCore 8 +sub_netcore8(){ + echo "configure netcore 8" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + wget -O - https://dot.net/v1/dotnet-install.sh | $SUDO_CMD bash -s -- --version 8.0.408 --install-dir /usr/local/bin + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache dotnet8-sdk + fi fi +} + +# V8 Repository +sub_v8repo(){ + echo "configure v8 from repository" + cd $ROOT_DIR - # V8 5.8 - if [ $INSTALL_V8REPO58 = 1 ]; then - $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv8-archived.list" - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install libicu57 libv8-5.8.283 libv8-5.8-dev + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends software-properties-common + + # V8 5.1 + if [ $INSTALL_V8REPO51 = 1 ]; then + $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv851.list" + $SUDO_CMD sh -c "echo \"deb http://archive.ubuntu.com/ubuntu trusty main\" > /etc/apt/sources.list.d/libicu52.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends --allow-unauthenticated libicu52 libv8-5.1.117 libv8-5.1-dev + fi + + # V8 5.4 + if [ $INSTALL_V8REPO54 = 1 ]; then + $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-5.4/ubuntu xenial main\" > /etc/apt/sources.list.d/libv854.list" + wget http://launchpadlibrarian.net/234847357/libicu55_55.1-7_amd64.deb + $SUDO_CMD dpkg -i libicu55_55.1-7_amd64.deb + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends --allow-unauthenticated libicu55 libv8-5.4-dev + $SUDO_CMD rm libicu55_55.1-7_amd64.deb + fi + + # V8 5.2 + if [ $INSTALL_V8REPO52 = 1 ]; then + $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.2 + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends libicu55 libv8-5.2-dev + fi + + # V8 5.7 + if [ $INSTALL_V8REPO57 = 1 ]; then + $SUDO_CMD add-apt-repository -y ppa:pinepain/libv8-5.7 + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends libicu55 libv8-5.7-dev + fi + + # V8 5.8 + if [ $INSTALL_V8REPO58 = 1 ]; then + $SUDO_CMD sh -c "echo \"deb http://ppa.launchpad.net/pinepain/libv8-archived/ubuntu trusty main\" > /etc/apt/sources.list.d/libv8-archived.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends libicu57 libv8-5.8.283 libv8-5.8-dev + fi fi } @@ -201,129 +432,469 @@ sub_v8repo(){ sub_v8(){ echo "configure v8" cd $ROOT_DIR - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install python - git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git - export PATH=`pwd`/depot_tools:"$PATH" - export GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends python + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + export PATH=`pwd`/depot_tools:"$PATH" + + export GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" - fetch v8 - cd v8 - git checkout 5.1-lkgr - gclient sync + fetch v8 + cd v8 + git checkout 5.1-lkgr + gclient sync - patch build/all.gyp $ROOT_DIR/nobuildtest.patch - GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" make library=shared native + patch build/all.gyp $ROOT_DIR/nobuildtest.patch + GYP_DEFINES="snapshot=on linux_use_bundled_gold=0 linux_use_gold_flags=0 component=shared_library" make -j$(getconf _NPROCESSORS_ONLN) library=shared native + fi } # NodeJS sub_nodejs(){ - # TODO: Review conflicts with Ruby Rails and NodeJS 4.x echo "configure nodejs" cd $ROOT_DIR - $SUDO_CMD apt-get update - - # Install python 2.7 to build node (gyp) - $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends install python build-essential libssl1.0.2 libssl1.0-dev - - # Install NodeJS from distributable (TODO: Keys not working) - NODE_VERSION=10.15.3 - PACKAGE_SUFFIX=tar.xz - - # for key in \ - # 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ - # FD3A5288F042B6850C66B31F09FE44734EB7990E \ - # 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ - # DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ - # C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ - # B9AE9905FFD7803F25714661B63B535A4C206CA9 \ - # 56730D5401028683275BD23C23EFEFE93C4CFFFE \ - # 77984A986EBC2AA786BC0F66B01FBB92821C587A \ - # 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600; \ - # do - # gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" \ - # || gpg --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" \ - # || gpg --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" - # done - - DPKG_ARCH="$(dpkg --print-architecture)" - - case "${DPKG_ARCH##*-}" in - amd64) ARCH='x64';; - ppc64el) ARCH='ppc64le';; - s390x) ARCH='s390x';; - arm64) ARCH='arm64';; - armhf) ARCH='armv7l';; - i386) ARCH='x86';; - *) echo "unsupported architecture ($DPKG_ARCH) for nodejs"; return 1;; - esac - - wget --no-check-certificate "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.$PACKAGE_SUFFIX" - wget --no-check-certificate "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" - # gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc - # grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - - tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner - rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc # SHASUMS256.txt - $SUDO_CMD ln -s /usr/local/bin/node /usr/local/bin/nodejs - - # Update npm and install node-gyp - npm i npm@latest -g - npm i node-gyp -g - - # # Install pkg config for icu library - # $SUDO_CMD apt-get -y --no-install-recommends install pkg-config - # pkg-config icu-i18n --cflags --libs + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ $INSTALL_C = 1 ]; then + # Required for test source/tests/metacall_node_port_c_lib_test + INSTALL_LIBGIT2="libgit2-dev" + else + INSTALL_LIBGIT2="" + fi + + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Note that Python is required for GYP + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends python3 g++ make nodejs npm curl $INSTALL_LIBGIT2 + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache python3 g++ make nodejs nodejs-dev npm curl $INSTALL_LIBGIT2 + + # Build dependencies (note libexecinfo-dev is not available in Alpine 3.17) + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main linux-headers libexecinfo libexecinfo-dev + + # Build NodeJS shared library + $SUDO_CMD apk add --no-cache --virtual .build-nodejs-deps \ + git \ + alpine-sdk \ + alpine-conf \ + ccache \ + brotli-dev \ + c-ares-dev \ + icu-dev \ + linux-headers \ + nghttp2-dev \ + openssl-dev \ + samurai \ + zlib-dev + + # Fix to a lower Python version (3.9) in order avoid conflicts with Python dependency of Clang from C Loader + $SUDO_CMD apk add --no-cache --virtual .build-nodejs-python-deps --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main \ + python3=3.9.16-r0 \ + py3-jinja2=3.0.1-r0 + + git clone --depth 1 --branch v3.17.3 https://git.alpinelinux.org/aports + cd aports/main/nodejs + sed -i 's/--shared-brotli\ \\/--shared \\\n --shared-brotli\ \\/g' APKBUILD + if [ "$SUDO_CMD" = "" ]; then + ABUILD_ROOT=-F + else + ABUILD_ROOT= + fi + echo "abuild-key" | abuild-keygen -a + $SUDO_CMD cp abuild-key.pub /etc/apk/keys + $SUDO_CMD abuild $ABUILD_ROOT checksum + $SUDO_CMD abuild $ABUILD_ROOT || true + cp pkg/nodejs/usr/bin/node /usr/bin/node + cp src/node-v18.14.2/out/Release/lib/libnode.so.108 /usr/lib/. + cd ../../.. + rm -rf aports + $SUDO_CMD apk del .build-nodejs-deps + $SUDO_CMD apk del .build-nodejs-python-deps + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + # Install NodeJS (required for source build or NPM itself) + brew install node@22 + # Make node 22 the default + brew link node@22 --force --overwrite + # Execute post install scripts + brew postinstall node@22 + # Define node location + NODE_PREFIX=$(brew --prefix node@22) + + # Configure NodeJS paths + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + + # Configure NPM path + echo "-DNPM_ROOT=$NODE_PREFIX/bin" >> $CMAKE_CONFIG_PATH + + # Make npm available for subsequent calls + export PATH="$NODE_PREFIX/bin:$PATH" + + # Build either using pre-compiled binaries or building node from source + if [ -z "${NodeJS_BUILD_FROM_SOURCE:-}" ]; then + # Define node location + NODE_PREFIX="$ROOT_DIR/build" + # Install NodeJS + wget -qO- https://github.com/metacall/libnode/releases/download/v22.9.0/libnode-${ARCHITECTURE}-macos.tar.xz | tar xvJ -C $NODE_PREFIX + # Configure NodeJS path + echo "-DNodeJS_EXECUTABLE=$NODE_PREFIX/node" >> $CMAKE_CONFIG_PATH + echo "-DNodeJS_LIBRARY=$NODE_PREFIX/libnode.dylib" >> $CMAKE_CONFIG_PATH + else + # Include binaries into PATH + export PATH="$NODE_PREFIX/bin:$PATH" + # Define executable path + echo "-DNodeJS_EXECUTABLE=$NODE_PREFIX/bin/node" >> $CMAKE_CONFIG_PATH + fi + + if [ $INSTALL_C = 1 ]; then + # Required for test source/tests/metacall_node_port_c_lib_test + brew install libgit2@1.8 + brew link libgit2@1.8 --force --overwrite + fi + fi +} + +# TypeScript +sub_typescript(){ + echo "configure typescript" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + # Install React dependencies in order to run the tests + $SUDO_CMD npm i react@latest -g + $SUDO_CMD npm i react-dom@latest -g + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + # Install React dependencies in order to run the tests + npm i react@latest -g + npm i react-dom@latest -g + fi +} + +# File +sub_file(){ + echo "configure file" +} + +# RPC +sub_rpc(){ + echo "cofingure rpc" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + # Install development files and documentation for libcurl (OpenSSL flavour) + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends libcurl4-openssl-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache curl-dev + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install curl + fi } # WebAssembly sub_wasm(){ echo "configure webassembly" - # TODO + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing wasmtime libwasmtime + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install wasmtime + fi +} - # $SUDO_CMD apt-get update - # $SUDO_CMD apt-get $APT_CACHE_CMD -y --no-install-recommends --fix-broken install lib32gcc-6-dev g++-multilib +# Java +sub_java(){ + echo "configure java" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends default-jdk default-jre + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache openjdk8 openjdk8-jre + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install openjdk@17 + sudo ln -sfn $(brew --prefix openjdk@17)/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk + JAVA_PREFIX=$(/usr/libexec/java_home -v 17) + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + echo "-DJAVA_HOME=$JAVA_PREFIX" >> $CMAKE_CONFIG_PATH + echo "-DJAVA_INCLUDE_PATH=$JAVA_PREFIX/include" >> $CMAKE_CONFIG_PATH + echo "-DJAVA_INCLUDE_PATH2=$JAVA_PREFIX/include/darwin" >> $CMAKE_CONFIG_PATH + echo "-DJAVA_AWT_INCLUDE_PATH=$JAVA_PREFIX/include" >> $CMAKE_CONFIG_PATH + fi } -# MetaCall -sub_metacall(){ - echo "configure metacall" +# C +sub_c(){ + echo "configure c" cd $ROOT_DIR - git clone --recursive https://github.com/metacall/core.git - mkdir core/build && cd core/build - cmake ../ -DPYTHON_EXECUTABLE=/usr/bin/python3.5 -DOPTION_BUILD_EXAMPLES=off -DOPTION_BUILD_LOADERS_PY=on -DOPTION_BUILD_LOADERS_RB=on -DOPTION_BUILD_LOADERS_CS=on -DOPTION_BUILD_LOADERS_JS=on -DCMAKE_BUILD_TYPE=Release -DDOTNET_CORE_PATH=/usr/share/dotnet/shared/Microsoft.NETCore.App/1.1.10/ - make - make test && echo "test ok!" + LLVM_VERSION_STRING=14 + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + # For now bookworm || trixie == sid, change when trixie is released + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ] || [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${VERSION_CODENAME}" = "trixie" ]; then + CODENAME="unstable" + LINKNAME="" + else + # "stable" Debian release + CODENAME="${VERSION_CODENAME}" + LINKNAME="-${CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + if [ -n "${CODENAME}" ]; then + LINKNAME="-${CODENAME}" + fi + fi + ;; + esac - echo "configure with cmake .. " + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | $SUDO_CMD tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + $SUDO_CMD sh -c "echo \"deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD sh -c "echo \"deb-src http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get install -y --no-install-recommends libffi-dev libclang-${LLVM_VERSION_STRING}-dev + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libffi-dev libclang-${LLVM_VERSION_STRING}-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libffi-dev + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main clang-libs=13.0.1-r1 clang-dev=13.0.1-r1 + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install libffi + brew install llvm@$LLVM_VERSION_STRING + brew link llvm@$LLVM_VERSION_STRING --force --overwrite + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + LIBCLANG_PREFIX=$(brew --prefix llvm@$LLVM_VERSION_STRING) + echo "-DLibClang_INCLUDE_DIR=${LIBCLANG_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DLibClang_LIBRARY=${LIBCLANG_PREFIX}/lib/libclang.dylib" >> $CMAKE_CONFIG_PATH + fi +} + +# Cobol +sub_cobol(){ + echo "configure cobol" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + echo "deb http://deb.debian.org/debian/ unstable main" | $SUDO_CMD tee -a /etc/apt/sources.list > /dev/null + + $SUDO_CMD apt-get update + $SUDO_CMD apt-get $APT_CACHE_CMD -t unstable install -y --no-install-recommends gnucobol + + # Remove unstable from sources.list + $SUDO_CMD head -n -2 /etc/apt/sources.list + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends gnucobol4 + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache --virtual .build-cobol-deps build-base tar libaio libnsl libc6-compat binutils abuild make gcc gmp-dev db-dev libxml2-dev ncurses-dev + + # Install gnucobol + wget https://sourceforge.net/projects/gnucobol/files/gnucobol/3.1/gnucobol-3.1-rc1.tar.gz/download -O gnucobol-3.1-rc1.tar.gz + tar xvfz gnucobol-3.1-rc1.tar.gz + cd gnucobol-3.1-rc1 + ./configure + make -j$(getconf _NPROCESSORS_ONLN) + make install + cd .. + rm -rf gnucobol-3.1-rc1 gnucobol-3.1-rc1.tar.gz + + # Clean build deps + $SUDO_CMD apk del .build-cobol-deps + + # Runtime deps + $SUDO_CMD apk add --no-cache db ncurses + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install gnucobol + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + COBOL_PREFIX=$(brew --prefix gnucobol) + echo "-DCOBOL_EXECUTABLE=${COBOL_PREFIX}/bin/cobc" >> $CMAKE_CONFIG_PATH + echo "-DCOBOL_INCLUDE_DIR=${COBOL_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DCOBOL_LIBRARY=${COBOL_PREFIX}/lib/libcob.dylib" >> $CMAKE_CONFIG_PATH + fi +} + +# Go +sub_go(){ + echo "configure go" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends golang + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache go + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install go + fi +} + +# Rust +sub_rust(){ + echo "configure rust" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends curl autoconf automake + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache curl musl-dev linux-headers libgcc + fi + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly-2021-12-04 --profile default + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly-2021-12-04 --profile default + brew install patchelf + fi } # Pack sub_pack(){ echo "configure pack" cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends rpm + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get $APT_CACHE_CMD install -y --no-install-recommends rpm + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache rpm + fi + fi } # Coverage sub_coverage(){ echo "configure coverage" cd $ROOT_DIR - $SUDO_CMD apt-get update - $SUDO_CMD apt-get install -y --no-install-recommends lcov + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends lcov python3 python3-pip + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache lcov python3 py3-pip + fi + + PIP_BREAK_SYSTEM_PACKAGES=`python3 -c 'import sys; sys.version_info.major >= 3 and sys.version_info.minor >= 11 and print("--break-system-packages", end="")'` + + pip3 install ${PIP_BREAK_SYSTEM_PACKAGES} gcovr==7.2 + fi +} + +# Clang format +sub_clangformat(){ + echo "configure clangformat" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + LLVM_VERSION_STRING=12 + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + # For now bookworm || trixie == sid, change when trixie is released + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ] || [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${VERSION_CODENAME}" = "trixie" ]; then + CODENAME="unstable" + LINKNAME="" + else + # "stable" Debian release + CODENAME="${VERSION_CODENAME}" + LINKNAME="-${CODENAME}" + fi + ;; + *) + # ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + if [ -n "${CODENAME}" ]; then + LINKNAME="-${CODENAME}" + fi + fi + ;; + esac + + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | $SUDO_CMD tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + $SUDO_CMD sh -c "echo \"deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD sh -c "echo \"deb-src http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD apt-get update + $SUDO_CMD apt-get install -y --no-install-recommends clang-format-${LLVM_VERSION_STRING} + $SUDO_CMD ln -s /usr/bin/clang-format-${LLVM_VERSION_STRING} /usr/bin/clang-format + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.15/main clang-extra-tools=12.0.1-r1 + fi + fi +} + +# Backtrace (this only improves stack traces verbosity but backtracing is enabled by default) +sub_backtrace(){ + echo "configure backtrace" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libdw-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache binutils-dev + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install dwarfutils + brew install libelf + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + LIBDWARD_PREFIX=$(brew --prefix dwarfutils) + LIBELF_PREFIX=$(brew --prefix libelf) + echo "-DLIBDWARF_INCLUDE_DIR=${LIBDWARD_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DLIBELF_LIBRARY=${LIBELF_PREFIX}/lib/libelf.a" >> $CMAKE_CONFIG_PATH + echo "-DLIBELF_INCLUDE_DIR=${LIBELF_PREFIX}/include" >> $CMAKE_CONFIG_PATH + fi +} + +# Sandbox (this provides sandboxing features in Linux through BFS filters with libseccomp) +sub_sandbox(){ + echo "configure sandbox" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libseccomp-dev + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libseccomp-dev + fi + fi } # Install sub_install(){ - if [ $RUN_AS_ROOT = 1 ]; then - SUDO_CMD="" - fi if [ $APT_CACHE = 1 ]; then - APT_CACHE_CMD=-o dir::cache::archives="$APT_CACHE_DIR" + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + APT_CACHE_CMD=-o dir::cache::archives="$APT_CACHE_DIR" + fi fi - if [ $INSTALL_APT = 1 ]; then - sub_apt + if [ $INSTALL_BASE = 1 ]; then + sub_base fi if [ $INSTALL_PYTHON = 1 ]; then sub_python @@ -334,12 +905,21 @@ sub_install(){ if [ $INSTALL_RAPIDJSON = 1 ]; then sub_rapidjson fi - if [ $INSTALL_FUNCHOOK = 1 ]; then - sub_funchook - fi if [ $INSTALL_NETCORE = 1 ]; then sub_netcore fi + if [ $INSTALL_NETCORE2 = 1 ]; then + sub_netcore2 + fi + if [ $INSTALL_NETCORE5 = 1 ]; then + sub_netcore5 + fi + if [ $INSTALL_NETCORE7 = 1 ]; then + sub_netcore7 + fi + if [ $INSTALL_NETCORE8 = 1 ]; then + sub_netcore8 + fi if [ $INSTALL_V8 = 1 ]; then sub_v8 fi @@ -349,14 +929,32 @@ sub_install(){ if [ $INSTALL_NODEJS = 1 ]; then sub_nodejs fi + if [ $INSTALL_TYPESCRIPT = 1 ]; then + sub_typescript + fi + if [ $INSTALL_FILE = 1 ]; then + sub_file + fi + if [ $INSTALL_RPC = 1 ]; then + sub_rpc + fi if [ $INSTALL_WASM = 1 ]; then sub_wasm fi - if [ $INSTALL_SWIG = 1 ]; then - sub_swig + if [ $INSTALL_JAVA = 1 ]; then + sub_java + fi + if [ $INSTALL_C = 1 ]; then + sub_c fi - if [ $INSTALL_METACALL = 1 ]; then - sub_metacall + if [ $INSTALL_COBOL = 1 ]; then + sub_cobol + fi + if [ $INSTALL_GO = 1 ]; then + sub_go + fi + if [ $INSTALL_RUST = 1 ]; then + sub_rust fi if [ $INSTALL_PACK = 1 ]; then sub_pack @@ -364,95 +962,157 @@ sub_install(){ if [ $INSTALL_COVERAGE = 1 ]; then sub_coverage fi - + if [ $INSTALL_CLANGFORMAT = 1 ]; then + sub_clangformat + fi + if [ $INSTALL_BACKTRACE = 1 ]; then + sub_backtrace + fi + if [ $INSTALL_SANDBOX = 1 ]; then + sub_sandbox + fi echo "install finished in workspace $ROOT_DIR" } # Configuration sub_options(){ - for var in "$@" + for option in "$@" do - if [ "$var" = 'root' ]; then - echo "running as root" - RUN_AS_ROOT=1 + if [ "$option" = 'cache' ]; then + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + echo "apt caching selected" + APT_CACHE=1 + fi fi - if [ "$var" = 'cache' ]; then - echo "apt caching selected" - APT_CACHE=1 + if [ "$option" = 'debug' ]; then + echo "Install dependencies in debug mode" + BUILD_TYPE=Debug fi - if [ "$var" = 'base' ]; then + if [ "$option" = 'release' ] || [ "$option" = 'relwithdebinfo' ]; then + echo "Install dependencies release mode" + BUILD_TYPE=Release + fi + if [ "$option" = 'base' ]; then echo "apt selected" - INSTALL_APT=1 + INSTALL_BASE=1 fi - if [ "$var" = 'python' ]; then + if [ "$option" = 'python' ]; then echo "python selected" INSTALL_PYTHON=1 fi - if [ "$var" = 'ruby' ]; then + if [ "$option" = 'ruby' ]; then echo "ruby selected" INSTALL_RUBY=1 fi - if [ "$var" = 'netcore' ]; then + if [ "$option" = 'netcore' ]; then echo "netcore selected" INSTALL_NETCORE=1 fi - if [ "$var" = 'rapidjson' ]; then + if [ "$option" = 'netcore2' ]; then + echo "netcore 2 selected" + INSTALL_NETCORE2=1 + fi + if [ "$option" = 'netcore5' ]; then + echo "netcore 5 selected" + INSTALL_NETCORE5=1 + fi + if [ "$option" = 'netcore7' ]; then + echo "netcore 7 selected" + INSTALL_NETCORE7=1 + fi + if [ "$option" = 'netcore8' ]; then + echo "netcore 8 selected" + INSTALL_NETCORE8=1 + fi + if [ "$option" = 'rapidjson' ]; then echo "rapidjson selected" INSTALL_RAPIDJSON=1 fi - if [ "$var" = 'funchook' ]; then - echo "funchook selected" - INSTALL_FUNCHOOK=1 - fi - if [ "$var" = 'v8' ] || [ "$var" = 'v8rep54' ]; then + if [ "$option" = 'v8' ] || [ "$option" = 'v8rep54' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO54=1 fi - if [ "$var" = 'v8rep57' ]; then + if [ "$option" = 'v8rep57' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO57=1 fi - if [ "$var" = 'v8rep58' ]; then + if [ "$option" = 'v8rep58' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO58=1 fi - if [ "$var" = 'v8rep52' ]; then + if [ "$option" = 'v8rep52' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO52=1 fi - if [ "$var" = 'v8rep51' ]; then + if [ "$option" = 'v8rep51' ]; then echo "v8 selected" INSTALL_V8REPO=1 INSTALL_V8REPO51=1 fi - if [ "$var" = 'nodejs' ]; then + if [ "$option" = 'nodejs' ]; then echo "nodejs selected" INSTALL_NODEJS=1 fi - if [ "$var" = 'wasm' ]; then + if [ "$option" = 'typescript' ]; then + echo "typescript selected" + INSTALL_TYPESCRIPT=1 + fi + if [ "$option" = 'file' ]; then + echo "file selected" + INSTALL_FILE=1 + fi + if [ "$option" = 'rpc' ]; then + echo "rpc selected" + INSTALL_RPC=1 + fi + if [ "$option" = 'wasm' ]; then echo "wasm selected" INSTALL_WASM=1 fi - if [ "$var" = 'swig' ]; then - echo "swig selected" - INSTALL_SWIG=1 + if [ "$option" = 'java' ]; then + echo "java selected" + INSTALL_JAVA=1 fi - if [ "$var" = 'metacall' ]; then - echo "metacall selected" - INSTALL_METACALL=1 + if [ "$option" = 'c' ]; then + echo "c selected" + INSTALL_C=1 fi - if [ "$var" = 'pack' ]; then + if [ "$option" = 'cobol' ]; then + echo "cobol selected" + INSTALL_COBOL=1 + fi + if [ "$option" = 'go' ]; then + echo "go selected" + INSTALL_GO=1 + fi + if [ "$option" = 'rust' ]; then + echo "rust selected" + INSTALL_RUST=1 + fi + if [ "$option" = 'pack' ]; then echo "pack selected" INSTALL_PACK=1 fi - if [ "$var" = 'coverage' ]; then + if [ "$option" = 'coverage' ]; then echo "coverage selected" INSTALL_COVERAGE=1 fi + if [ "$option" = 'clangformat' ]; then + echo "clangformat selected" + INSTALL_CLANGFORMAT=1 + fi + if [ "$option" = 'backtrace' ]; then + echo "backtrace selected" + INSTALL_BACKTRACE=1 + fi + if [ "$option" = 'sandbox' ]; then + echo "sandbox selected" + INSTALL_SANDBOX=1 + fi done } @@ -460,25 +1120,36 @@ sub_options(){ sub_help() { echo "Usage: `basename "$0"` list of component" echo "Components:" - echo " root" + echo " debug | release | relwithdebinfo" echo " cache" echo " base" echo " python" echo " ruby" echo " netcore" + echo " netcore2" + echo " netcore5" + echo " netcore7" + echo " netcore8" echo " rapidjson" - echo " funchook" echo " v8" echo " v8rep51" echo " v8rep54" echo " v8rep57" echo " v8rep58" echo " nodejs" + echo " typescript" + echo " file" + echo " rpc" echo " wasm" - echo " swig" - echo " metacall" + echo " java" + echo " c" + echo " cobol" + echo " go" echo " pack" echo " coverage" + echo " clangformat" + echo " backtrace" + echo " sandbox" echo "" } diff --git a/tools/metacall-license.sh b/tools/metacall-license.sh index 59e6207980..2be232899b 100755 --- a/tools/metacall-license.sh +++ b/tools/metacall-license.sh @@ -4,7 +4,7 @@ # MetaCall License Bash Script by Parra Studios # License bash script utility for MetaCall. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ # limitations under the License. # +set -euxo pipefail + # Warning echo "WARNING: Do not run the script multiple times. Uncomment the 'exit 0' in the code to continue." exit 0 @@ -33,7 +35,7 @@ find "$EXEC_PATH" -type f \ -exec sh -c ' \ # Copyright - COPYRIGHT="Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia $" + COPYRIGHT="Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia $" # License LICENSE=$(cat <<-END diff --git a/tools/metacall-runtime.sh b/tools/metacall-runtime.sh index f036d86e1d..9ef4afba2d 100755 --- a/tools/metacall-runtime.sh +++ b/tools/metacall-runtime.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # -# MetaCall Configuration Environment Bash Script by Parra Studios +# MetaCall Configuration Environment Shell Script by Parra Studios # Configure and install MetaCall environment script utility. # -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,21 +19,61 @@ # limitations under the License. # +set -euxo + ROOT_DIR=$(pwd) -RUN_AS_ROOT=0 -SUDO_CMD=sudo +BUILD_TYPE=Release INSTALL_APT=1 INSTALL_PYTHON=0 INSTALL_RUBY=0 INSTALL_NETCORE=0 +INSTALL_NETCORE2=0 +INSTALL_NETCORE5=0 INSTALL_V8=0 INSTALL_NODEJS=0 +INSTALL_TYPESCRIPT=0 +INSTALL_FILE=0 +INSTALL_RPC=0 +INSTALL_WASM=0 +INSTALL_JAVA=0 +INSTALL_C=0 +INSTALL_COBOL=0 +INSTALL_BACKTRACE=0 +INSTALL_SANDBOX=0 INSTALL_PORTS=0 INSTALL_CLEAN=0 SHOW_HELP=0 PROGNAME=$(basename $0) +# Operative System detection +case "$(uname -s)" in + Linux*) OPERATIVE_SYSTEM=Linux;; + Darwin*) OPERATIVE_SYSTEM=Darwin;; + CYGWIN*) OPERATIVE_SYSTEM=Cygwin;; + MINGW*) OPERATIVE_SYSTEM=MinGW;; + *) OPERATIVE_SYSTEM="Unknown" +esac + +# Check out for sudo +if [ "`id -u`" = '0' ]; then + SUDO_CMD="" +else + SUDO_CMD=sudo +fi + +# Linux Distro detection +if [ -f /etc/os-release ]; then # Either Debian or Ubuntu + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_DISTRO=$(cat /etc/os-release | grep "^ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') + # Cat file | Get the ID field | Remove 'ID=' | Remove leading and trailing spaces | Remove quotes + LINUX_VERSION_ID=$(cat /etc/os-release | grep "^VERSION_ID=" | cut -f2- -d= | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '"') +else + # TODO: Implement more distros or better detection + LINUX_DISTRO=unknown + LINUX_VERSION_ID=unknown +fi + # Install and mark packages to avoid autoremove sub_apt_install_hold(){ $SUDO_CMD apt-get -y install --no-install-recommends $@ @@ -44,34 +84,40 @@ sub_apt_install_hold(){ sub_apt(){ echo "configure apt" cd $ROOT_DIR - $SUDO_CMD apt-get update && apt-get -y install --no-install-recommends wget + $SUDO_CMD apt-get update && apt-get -y install --no-install-recommends wget gpg apt-transport-https } # Python sub_python(){ echo "configure python" cd $ROOT_DIR - sub_apt_install_hold python3 libpython3.5 + + if [ "${BUILD_TYPE}" = "Debug" ]; then + sub_apt_install_hold libpython3-dbg + else + sub_apt_install_hold libpython3-dev + fi } # Ruby sub_ruby(){ echo "configure ruby" cd $ROOT_DIR - sub_apt_install_hold ruby2.3 libruby2.3 \ - libgdbm3 libncurses5 libssl1.0.2 libyaml-0-2 rake ruby ruby-did-you-mean \ - ruby-minitest ruby-net-telnet ruby-power-assert ruby-test-unit \ - rubygems-integration ca-certificates + + $SUDO_CMD apt-get update + sub_apt_install_hold ruby libruby } # NetCore sub_netcore(){ echo "configure netcore" cd $ROOT_DIR + + # Debian Stretch sub_apt_install_hold libc6 libcurl3 libgcc1 libgssapi-krb5-2 libicu57 \ liblttng-ust0 libssl1.0.2 libstdc++6 libunwind8 libuuid1 zlib1g ca-certificates - # Install .NET Core + # Install .NET Core Runtime 1.x DOTNET_VERSION=1.1.10 DOTNET_DOWNLOAD_URL=https://dotnetcli.blob.core.windows.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-debian.9-x64.$DOTNET_VERSION.tar.gz @@ -82,6 +128,57 @@ sub_netcore(){ ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet } +# NetCore 2 +sub_netcore2(){ + echo "configure netcore 2" + cd $ROOT_DIR + + # Install NET Core Runtime 2.x + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + $SUDO_CMD apt-get update + sub_apt_install_hold dotnet-runtime-2.2=2.2.8-1 +} + +# NetCore 5 +sub_netcore5(){ + echo "configure netcore 5" + cd $ROOT_DIR + + # Install NET Core Runtime 5.x + wget https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + $SUDO_CMD apt-get update + sub_apt_install_hold dotnet-runtime-5.0=5.0.17-1 +} + +# NetCore 7 +sub_netcore7(){ + echo "configure netcore 7" + cd $ROOT_DIR + + # Install NET Core Runtime 7.x + wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + $SUDO_CMD dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + + $SUDO_CMD apt-get update + sub_apt_install_hold dotnet-runtime-7.0=7.0.5-1 +} + +# NetCore 8 +sub_netcore8(){ + echo "configure netcore 8" + cd $ROOT_DIR + + # Install NET Core Runtime 8.x + wget -O - https://dot.net/v1/dotnet-install.sh | $SUDO_CMD bash -s -- --version 8.0.408 --install-dir /usr/local/bin --runtime dotnet +} + # V8 sub_v8(){ echo "configure v8" @@ -91,11 +188,210 @@ sub_v8(){ # NodeJS sub_nodejs(){ echo "configure node" + + # Install NodeJS library + sub_apt_install_hold libnode-dev +} + +# TypeScript +sub_typescript(){ + echo "configure typescript" # Nothing needed, node_modules are local to the path, # runtime is located in /usr/local/lib, and node builtins # are already compiled in the runtime } +# File +sub_file(){ + echo "configure file" + # Nothing needed +} + +# RPC +sub_rpc(){ + echo "configure rpc" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ]; then + CODENAME="unstable" + else + CODENAME="${VERSION_CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + fi + ;; + esac + + if [ "${CODENAME}" = "trixie" ] || [ "${CODENAME}" = "noble" ] || [ "${CODENAME}" = "unstable" ]; then + sub_apt_install_hold libcurl4t64 + else + sub_apt_install_hold libcurl4 + fi + fi + fi +} + +# WebAssembly +sub_wasm(){ + echo "configure wasm" + + # TODO +} + +# Java +sub_java(){ + echo "configure java" + + sub_apt_install_hold default-jre +} + +# C +sub_c(){ + echo "configure c" + cd $ROOT_DIR + LLVM_VERSION_STRING=14 + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + # For now bookworm || trixie == sid, change when trixie is released + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ] || [ "${VERSION_CODENAME}" = "bookworm" ] || [ "${VERSION_CODENAME}" = "trixie" ]; then + CODENAME="unstable" + LINKNAME="" + else + # "stable" Debian release + CODENAME="${VERSION_CODENAME}" + LINKNAME="-${CODENAME}" + fi + ;; + *) + # ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + if [ -n "${CODENAME}" ]; then + LINKNAME="-${CODENAME}" + fi + fi + ;; + esac + + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | $SUDO_CMD apt-key add + $SUDO_CMD sh -c "echo \"deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD sh -c "echo \"deb-src http://apt.llvm.org/${CODENAME}/ llvm-toolchain${LINKNAME}-${LLVM_VERSION_STRING} main\" >> /etc/apt/sources.list" + $SUDO_CMD apt-get update + sub_apt_install_hold libffi libclang-${LLVM_VERSION_STRING} + elif [ "${LINUX_DISTRO}" = "ubuntu" ]; then + sub_apt_install_hold libffi libclang-${LLVM_VERSION_STRING} + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libffi-dev + $SUDO_CMD apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/v3.16/main clang-libs=13.0.1-r1 clang-dev=13.0.1-r1 + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install libffi + brew install llvm@$LLVM_VERSION_STRING + brew link llvm@$LLVM_VERSION_STRING --force --overwrite + mkdir -p "$ROOT_DIR/build" + CMAKE_CONFIG_PATH="$ROOT_DIR/build/CMakeConfig.txt" + LIBCLANG_PREFIX=$(brew --prefix llvm@$LLVM_VERSION_STRING) + echo "-DLibClang_INCLUDE_DIR=${LIBCLANG_PREFIX}/include" >> $CMAKE_CONFIG_PATH + echo "-DLibClang_LIBRARY=${LIBCLANG_PREFIX}/lib/libclang.dylib" >> $CMAKE_CONFIG_PATH + fi +} + +# Cobol +sub_cobol(){ + echo "configure cobol" + + if [ "${LINUX_DISTRO}" == "debian" ]; then + echo "deb http://deb.debian.org/debian/ unstable main" | $SUDO_CMD tee -a /etc/apt/sources.list > /dev/null + + $SUDO_CMD apt-get update + sub_apt_install_hold libcob4 + + # Remove unstable from sources.list + $SUDO_CMD head -n -2 /etc/apt/sources.list + $SUDO_CMD apt-get update + elif [ "${LINUX_DISTRO}" == "ubuntu" ]; then + sub_apt_install_hold libcob4 + fi +} + +# Backtrace (this only improves stack traces verbosity but backtracing is enabled by default) +sub_backtrace(){ + echo "configure backtrace" + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + UBUNTU_CODENAME="" + CODENAME_FROM_ARGUMENTS="" + + # Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) + . /etc/os-release + + case ${LINUX_DISTRO} in + debian) + if [ "${VERSION:-}" = "unstable" ] || [ "${VERSION:-}" = "testing" ]; then + CODENAME="unstable" + else + CODENAME="${VERSION_CODENAME}" + fi + ;; + *) + # Ubuntu and its derivatives + if [ -n "${UBUNTU_CODENAME}" ]; then + CODENAME="${UBUNTU_CODENAME}" + fi + ;; + esac + + if [ "${CODENAME}" = "trixie" ] || [ "${CODENAME}" = "noble" ] || [ "${CODENAME}" = "unstable" ]; then + sub_apt_install_hold libdw1t64 libelf1t64 + else + sub_apt_install_hold libdw1 + fi + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache binutils + fi + elif [ "${OPERATIVE_SYSTEM}" = "Darwin" ]; then + brew install dwarfutils + brew install libelf + fi +} + +# Sandbox (this provides sandboxing features in Linux through BFS filters with libseccomp) +sub_sandbox(){ + echo "configure sandbox" + cd $ROOT_DIR + + if [ "${OPERATIVE_SYSTEM}" = "Linux" ]; then + if [ "${LINUX_DISTRO}" = "debian" ] || [ "${LINUX_DISTRO}" = "ubuntu" ]; then + $SUDO_CMD apt-get install -y --no-install-recommends libseccomp + elif [ "${LINUX_DISTRO}" = "alpine" ]; then + $SUDO_CMD apk add --no-cache libseccomp + fi + fi +} + # Ports sub_ports(){ echo "configure ports" @@ -105,9 +401,6 @@ sub_ports(){ # Install sub_install(){ - if [ $RUN_AS_ROOT = 1 ]; then - SUDO_CMD="" - fi if [ $INSTALL_APT = 1 ]; then sub_apt fi @@ -120,12 +413,45 @@ sub_install(){ if [ $INSTALL_NETCORE = 1 ]; then sub_netcore fi + if [ $INSTALL_NETCORE2 = 1 ]; then + sub_netcore2 + fi + if [ $INSTALL_NETCORE5 = 1 ]; then + sub_netcore5 + fi if [ $INSTALL_V8 = 1 ]; then sub_v8 fi if [ $INSTALL_NODEJS = 1 ]; then sub_nodejs fi + if [ $INSTALL_TYPESCRIPT = 1 ]; then + sub_typescript + fi + if [ $INSTALL_FILE = 1 ]; then + sub_file + fi + if [ $INSTALL_RPC = 1 ]; then + sub_rpc + fi + if [ $INSTALL_WASM = 1 ]; then + sub_wasm + fi + if [ $INSTALL_JAVA = 1 ]; then + sub_java + fi + if [ $INSTALL_C = 1 ]; then + sub_c + fi + if [ $INSTALL_COBOL = 1 ]; then + sub_cobol + fi + if [ $INSTALL_BACKTRACE = 1 ]; then + sub_backtrace + fi + if [ $INSTALL_SANDBOX = 1 ]; then + sub_sandbox + fi if [ $INSTALL_PORTS = 1 ]; then sub_ports fi @@ -140,48 +466,95 @@ sub_install(){ sub_clean(){ echo "clean dependencies" - $SUDO_CMD apt-get -y remove wget + $SUDO_CMD apt-get -y remove wget gpg $SUDO_CMD apt-get -y autoclean - $SUDO_CMD apt-get -y autoremove } # Configuration sub_options(){ - for var in "$@" + for option in "$@" do - if [ "$var" = 'root' ]; then - echo "running as root" - RUN_AS_ROOT=1 + if [ "$option" = 'debug' ]; then + echo "debug mode selected" + BUILD_TYPE=Debug fi - if [ "$var" = 'base' ]; then + if [ "$option" = 'release' ] || [ "$option" = 'relwithdebinfo' ]; then + echo "release mode selected" + BUILD_TYPE=Release + fi + if [ "$option" = 'base' ]; then echo "apt selected" INSTALL_APT=1 fi - if [ "$var" = 'python' ]; then + if [ "$option" = 'python' ]; then echo "python selected" INSTALL_PYTHON=1 fi - if [ "$var" = 'ruby' ]; then + if [ "$option" = 'ruby' ]; then echo "ruby selected" INSTALL_RUBY=1 fi - if [ "$var" = 'netcore' ]; then + if [ "$option" = 'netcore' ]; then echo "netcore selected" INSTALL_NETCORE=1 fi - if [ "$var" = 'v8' ]; then + if [ "$option" = 'netcore2' ]; then + echo "netcore 2 selected" + INSTALL_NETCORE2=1 + fi + if [ "$option" = 'netcore5' ]; then + echo "netcore 5 selected" + INSTALL_NETCORE5=1 + fi + if [ "$option" = 'v8' ]; then echo "v8 selected" INSTALL_V8=1 fi - if [ "$var" = 'nodejs' ]; then + if [ "$option" = 'nodejs' ]; then echo "nodejs selected" INSTALL_NODEJS=1 fi - if [ "$var" = 'ports' ]; then + if [ "$option" = 'typescript' ]; then + echo "typescript selected" + INSTALL_TYPESCRIPT=1 + fi + if [ "$option" = 'file' ]; then + echo "file selected" + INSTALL_FILE=1 + fi + if [ "$option" = 'rpc' ]; then + echo "rpc selected" + INSTALL_RPC=1 + fi + if [ "$option" = 'wasm' ]; then + echo "wasm selected" + INSTALL_WASM=1 + fi + if [ "$option" = 'java' ]; then + echo "java selected" + INSTALL_JAVA=1 + fi + if [ "$option" = 'c' ]; then + echo "c selected" + INSTALL_C=1 + fi + if [ "$option" = 'cobol' ]; then + echo "cobol selected" + INSTALL_COBOL=1 + fi + if [ "$option" = 'backtrace' ]; then + echo "backtrace selected" + INSTALL_BACKTRACE=1 + fi + if [ "$option" = 'sandbox' ]; then + echo "sandbox selected" + INSTALL_SANDBOX=1 + fi + if [ "$option" = 'ports' ]; then echo "ports selected" INSTALL_PORTS=1 fi - if [ "$var" = 'clean' ]; then + if [ "$option" = 'clean' ]; then echo "clean selected" INSTALL_CLEAN=1 fi @@ -192,13 +565,23 @@ sub_options(){ sub_help() { echo "Usage: `basename "$0"` list of component" echo "Components:" - echo " root" + echo " debug | release | relwithdebinfo" echo " base" echo " python" echo " ruby" echo " netcore" + echo " netcore2" echo " v8" echo " nodejs" + echo " typescript" + echo " file" + echo " rpc" + echo " wasm" + echo " java" + echo " c" + echo " cobol" + echo " backtrace" + echo " sandbox" echo " ports" echo " clean" echo "" diff --git a/tools/metacall-sanitizer.sh b/tools/metacall-sanitizer.sh new file mode 100755 index 0000000000..cd3f08b402 --- /dev/null +++ b/tools/metacall-sanitizer.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# +# MetaCall Sanitizer Bash Script by Parra Studios +# Install, build and sanitizer test bash script utility for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -euxo pipefail + +BUILD_SANITIZER=${1:-address-sanitizer} +BUILD_LANGUAGES=( + python ruby netcore8 nodejs typescript file rpc wasm java c cobol rust +) +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) +ROOT_DIR=$(dirname "$SCRIPT_DIR") +BUILD_DIR="${ROOT_DIR}/build" + +if [ "${BUILD_SANITIZER}" != "address-sanitizer" ] && [ "${BUILD_SANITIZER}" != "thread-sanitizer" ] && [ "${BUILD_SANITIZER}" != "memory-sanitizer" ]; then + echo "Sanitizer '${BUILD_SANITIZER}' not supported, use 'address-sanitizer' or 'thread-sanitizer' or 'memory-sanitizer'." + exit 1 +fi + +# Install +"${SCRIPT_DIR}/metacall-environment.sh" base ${BUILD_LANGUAGES[@]} rapidjson pack backtrace + +# Configure and Build +export NODE_PATH="/usr/lib/node_modules" +export LOADER_LIBRARY_PATH="${BUILD_DIR}" +export LOADER_SCRIPT_PATH="${BUILD_DIR}/scripts" +export CONFIGURATION_PATH="${BUILD_DIR}/configurations/global.json" +export SERIAL_LIBRARY_PATH="${BUILD_DIR}" +export DETOUR_LIBRARY_PATH="${BUILD_DIR}" + +BUILD_OPTIONS=( + ${BUILD_SANITIZER} debug ${BUILD_LANGUAGES[@]} examples tests scripts ports install pack benchmarks +) + +mkdir -p ${BUILD_DIR} +cd ${BUILD_DIR} +"${SCRIPT_DIR}/metacall-configure.sh" ${BUILD_OPTIONS[@]} +"${SCRIPT_DIR}/metacall-build.sh" ${BUILD_OPTIONS[@]} diff --git a/tools/metacall-tests-find-deadlocks.js b/tools/metacall-tests-find-deadlocks.js new file mode 100644 index 0000000000..d5ffd834fd --- /dev/null +++ b/tools/metacall-tests-find-deadlocks.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node +'use strict'; + +/* This script gets the output of ctest and returns + * the non-finished tests or the failed ones, useful + * for detecting deadlocks. Use it like: + * $ ctest -VV &> ctest_output_file.txt + * $ node metacall-tests-find-deadlocks.js ctest_output_file.txt + */ + +const { readFileSync } = require('fs'); + +if (!process.argv[2]) { + console.error('No file specified'); + process.exit(1); +} + +const file = readFileSync(process.argv[2]); + +if (!file) { + console.error('Failed to read the file'); + process.exit(2); +} + +const tests = file.toString().split('\n').reduce((tests, line) => { + const splitted = line.split(' ').filter(element => element); + if (splitted[0] === 'Start') { + tests.start.push(splitted[2]); + } else if (splitted[5] === 'Passed') { + tests.finish.push(splitted[3]); + } + return tests; +}, { start: [], finish: [] }); + +const deadlock = tests.start.filter(x => !tests.finish.includes(x)); + +console.log(deadlock); diff --git a/tools/node/.dockerignore b/tools/node/.dockerignore deleted file mode 100644 index 23b2e71136..0000000000 --- a/tools/node/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -** -!tools/metacall-environment.sh -!cmake/FindNodeJS.cmake diff --git a/tools/node/Dockerfile b/tools/node/Dockerfile deleted file mode 100644 index a651f100a9..0000000000 --- a/tools/node/Dockerfile +++ /dev/null @@ -1,73 +0,0 @@ -# -# MetaCall Library by Parra Studios -# Docker image infrastructure for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Configure MetaCall Depends node image -ARG METACALL_BASE_IMAGE - -# MetaCall Depends node builder image -FROM ${METACALL_BASE_IMAGE} AS builder - -# Image descriptor -LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ - copyright.address="vic798@gmail.com" \ - maintainer.name="Vicente Eduardo Ferrer Garcia" \ - maintainer.address="vic798@gmail.com" \ - vendor="MetaCall Inc." \ - version="0.1" - -# Arguments -ARG METACALL_PATH - -# Environment variables -ENV DEBIAN_FRONTEND=noninteractive - -# Define working directory -WORKDIR $METACALL_PATH - -# Copy MetaCall environment script -COPY tools/metacall-environment.sh $METACALL_PATH/tools/ - -# Install build dependencies -RUN tools/metacall-environment.sh root base nodejs - -# Copy MetaCall NodeJS cmake script -COPY cmake/FindNodeJS.cmake $METACALL_PATH/cmake/FindNodeJS.cmake - -# Run NodeJS cmake script (TODO: Make find library prefixes/suffixes generic) -ARG METACALL_NODE_BUILD_TYPE - -RUN cmake \ - -DCMAKE_BUILD_TYPE=${METACALL_NODE_BUILD_TYPE} \ - -DCMAKE_FIND_LIBRARY_PREFIXES=lib \ - -DCMAKE_FIND_LIBRARY_SUFFIXES=".a;.so" \ - -P $METACALL_PATH/cmake/FindNodeJS.cmake - -# MetaCall Depends node image -FROM scratch AS deps_node - -# Image descriptor -LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ - copyright.address="vic798@gmail.com" \ - maintainer.name="Vicente Eduardo Ferrer Garcia" \ - maintainer.address="vic798@gmail.com" \ - vendor="MetaCall Inc." \ - version="0.1" - -# Copy MetaCall NodeJS dependencies -COPY --from=builder /usr/local/lib/libnode.so.* /usr/local/lib/ diff --git a/tools/node/hooks/build b/tools/node/hooks/build deleted file mode 100755 index 5468e8b335..0000000000 --- a/tools/node/hooks/build +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load environment variables -source ./hooks/env - -# Show message -echo "[***] Build hook running" -echo "[***] - Building $IMAGE_NAME from dockerfile $DOCKERFILE_PATH" -echo "[***] - Running build script from `pwd`" - -# Link docker ignore for the context -ln -sf "`pwd`/.dockerignore" ../../.dockerignore - -# TODO -# --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ -# --build-arg "SOURCE_COMMIT=$GIT_SHA1" \ -# --build-arg "DOCKERFILE_PATH=$DOCKERFILE_PATH" \ -# --build-arg "SOURCE_TYPE=$SOURCE_TYPE" \ -# ${VERSION:+--build-arg "VERSION=$VERSION"} \ - -# Build node image -docker build \ - --cache-from registry.hub.docker.com/$IMAGE_NAME \ - --build-arg "METACALL_BASE_IMAGE=$METACALL_BASE_IMAGE" \ - --build-arg "METACALL_PATH=$METACALL_PATH" \ - --build-arg "METACALL_NODE_BUILD_TYPE=$METACALL_NODE_BUILD_TYPE" \ - -t $IMAGE_NAME \ - -f $DOCKERFILE_PATH ../.. diff --git a/tools/node/hooks/env b/tools/node/hooks/env deleted file mode 100755 index a8e3e1bc0b..0000000000 --- a/tools/node/hooks/env +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -# -# MetaCall Build Bash Script by Parra Studios -# Build and install bash script utility for MetaCall. -# -# Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Load common environment variables -source ../../hooks/env - -# Node environment variables -DEBIAN_FRONTEND=noninteractive - -# Override docker image -IMAGE_NAME=metacall/core:deps_node diff --git a/tools/core/.dockerignore b/tools/runtime/.dockerignore similarity index 100% rename from tools/core/.dockerignore rename to tools/runtime/.dockerignore diff --git a/tools/runtime/Dockerfile b/tools/runtime/Dockerfile new file mode 100644 index 0000000000..4ac47f5184 --- /dev/null +++ b/tools/runtime/Dockerfile @@ -0,0 +1,103 @@ +# +# MetaCall Library by Parra Studios +# Docker image infrastructure for MetaCall. +# +# Copyright (C) 2016 - 2025 Vicente Eduardo Ferrer Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Configure MetaCall base image +ARG METACALL_BASE_IMAGE + +# MetaCall Depends builder image +FROM metacall/core:dev AS builder + +# Image descriptor +LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ + copyright.address="vic798@gmail.com" \ + maintainer.name="Vicente Eduardo Ferrer Garcia" \ + maintainer.address="vic798@gmail.com" \ + vendor="MetaCall Inc." \ + version="0.1" + +# Arguments +ARG METACALL_PATH + +# Environment variables +ENV DEBIAN_FRONTEND=noninteractive \ + DOTNET_CLI_TELEMETRY_OPTOUT=true + +# Define working directory +WORKDIR $METACALL_PATH + +# MetaCall image +FROM ${METACALL_BASE_IMAGE} AS core + +# Image descriptor +LABEL copyright.name="Vicente Eduardo Ferrer Garcia" \ + copyright.address="vic798@gmail.com" \ + maintainer.name="Vicente Eduardo Ferrer Garcia" \ + maintainer.address="vic798@gmail.com" \ + vendor="MetaCall Inc." \ + version="0.1" + +# Arguments +ARG METACALL_PATH + +# Environment variables +ENV LOADER_LIBRARY_PATH=/usr/local/lib \ + LOADER_SCRIPT_PATH=/usr/local/scripts \ + CONFIGURATION_PATH=/usr/local/share/metacall/configurations/global.json \ + SERIAL_LIBRARY_PATH=/usr/local/lib \ + DETOUR_LIBRARY_PATH=/usr/local/lib \ + DEBIAN_FRONTEND=noninteractive \ + NODE_PATH=/usr/local/lib/node_modules \ + DOTNET_CLI_TELEMETRY_OPTOUT=true + +# Define working directory +WORKDIR $METACALL_PATH + +# Copy MetaCall dependecies +COPY tools/metacall-runtime.sh $METACALL_PATH + +# Configure MetaCall runtime tool script +ARG METACALL_RUNTIME_OPTIONS + +# Install runtimes +RUN mkdir -p /usr/local/scripts \ + && chmod 500 $METACALL_PATH/metacall-runtime.sh \ + && $METACALL_PATH/metacall-runtime.sh ${METACALL_RUNTIME_OPTIONS} \ + && rm -rf $METACALL_PATH/metacall-runtime.sh + +# Copy libraries from builder +COPY --from=builder /usr/local/lib/ /usr/local/lib/ + +# Delete unwanted files from libraries +RUN ls /usr/local/lib/ \ + | grep -v '.*\.so$' \ + | grep -v '.*\.so.*' \ + | grep -v '.*\.dll$' \ + | grep -v '.*\.js$' \ + | grep -v '.*\.ts$' \ + | grep -v '.*\.node$' \ + | grep -v '^plugins$' \ + | grep -v '^node_modules$' \ + | grep -v '^python3\..*' \ + | xargs rm -rf + +# Copy headers from builder +COPY --from=builder /usr/local/include/metacall /usr/local/include/metacall + +# Copy configuration from builder +COPY --from=builder /usr/local/share/metacall/configurations/* /usr/local/share/metacall/configurations/