From 0e39f684b65d3b573a48d745202faa2c208190eb Mon Sep 17 00:00:00 2001 From: Kirill Golikov Date: Wed, 13 Aug 2025 17:43:01 +0300 Subject: [PATCH 1/7] fix: resolve enum constant name collisions and add side-effect reflect import --- generator/generator.py | 115 +++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 44 deletions(-) diff --git a/generator/generator.py b/generator/generator.py index d141a53..2bad330 100644 --- a/generator/generator.py +++ b/generator/generator.py @@ -827,7 +827,7 @@ def gen_delegate_body(prototype: dict, delegates: set[str]) -> str: return '\n'.join(delegate_code) -def gen_enum_body(enum: dict, enum_type: str, enums: set[str]) -> str: +def gen_enum_body(enum: dict, enum_type: str, enums: set[str], used_names: set[str]) -> str: """ Generates Go-style constant definitions for an enum. """ @@ -843,30 +843,61 @@ def gen_enum_body(enum: dict, enum_type: str, enums: set[str]) -> str: # Add the enum name to the set enums.add(enum_name) + underlying = convert_type(enum_type) + # Start building the Go enum definition - enum_code = [] + enum_code: list[str] = [] # Add enum description if enum_description: enum_code.append(f'// {enum_name} - {enum_description}') - enum_code.append(f'type {enum_name} = {convert_type(enum_type)}\n') + enum_code.append(f'type {enum_name} = {underlying}\n') # Define constants enum_code.append('const (') - for i, value in enumerate(enum_values): - name = value.get('name', 'InvalidName') - enum_value = value.get('value', str(i)) - description = value.get('description', '') - # Add comment for each constant - if description: - enum_code.append(f'\t// {name} - {description}') - enum_code.append(f'\t{name} {enum_name} = {enum_value}') + local_seen: dict[str, int] = {} - # Close the enum definition - enum_code.append(')') + for i, value in enumerate(enum_values): + raw_name = value.get('name', f'Value{i}') + base_name = generate_name(raw_name) + enum_val = value.get('value', str(i)) + descr = value.get('description', '') + + ## Counter for local repeats + local_seen[base_name] = local_seen.get(base_name, 0) + 1 + is_local_duplicate = local_seen[base_name] > 1 + + candidate = base_name + + def resolve_conflict(name: str) -> str: + if name not in used_names: + return name + # trying with prefix + pref = f'{name}_{enum_name}' + if pref not in used_names: + return pref + # if the prefix is also occupied, add suffixes + suffix = 2 + while True: + trial = f'{pref}_{suffix}' + if trial not in used_names: + return trial + suffix += 1 + + # If it is already globally occupied or a local duplicate, we run the logic of the conflict. + if candidate in used_names or is_local_duplicate: + candidate = resolve_conflict(candidate) + + # Registration + used_names.add(candidate) + + if descr: + enum_code.append(f'\t// {raw_name} - {descr}') + enum_code.append(f'\t{candidate} {enum_name} = {enum_val}') + enum_code.append(')') return '\n'.join(enum_code) @@ -1036,46 +1067,42 @@ def process_prototype(prototype: dict): def generate_enum_code(pplugin: dict, enums: set[str]) -> str: """ - Generate Go enum code from a plugin definition. + Iterates over all methods and collects enums. + used_names — global set of already occupied identifiers (constants). """ - # Container for all generated enum code - content = [] + content: list[str] = [] + used_names: set[str] = set() + + def process_enum(e: dict, etype: str): + code = gen_enum_body(e, etype, enums, used_names) + if code: + content.append(code) + + def process_prototype(proto: dict): + # Return type + if 'enum' in proto.get('retType', {}): + rt = proto['retType'] + process_enum(rt['enum'], rt.get('type', '')) + # Parameters + for p in proto.get('paramTypes', []): + if 'enum' in p: + process_enum(p['enum'], p.get('type', '')) + if 'prototype' in p: + process_prototype(p['prototype']) - def process_enum(enum_data: dict, enum_type: str): - """ - Generate enum code from the given enum data if it hasn't been processed. - """ - enum_code = gen_enum_body(enum_data, enum_type, enums) - if enum_code: - content.append(enum_code) - - def process_prototype(prototype: dict): - """ - Recursively process a function prototype for enums. - """ - if 'enum' in prototype.get('retType', {}): - process_enum(prototype['retType']['enum'], prototype['retType'].get('type', '')) - - for param in prototype.get('paramTypes', []): - if 'enum' in param: - process_enum(param['enum'], param.get('type', '')) - if 'prototype' in param: # Process nested prototypes - process_prototype(param['prototype']) - - # Main loop: Process all exported methods in the plugin for method in pplugin.get('exportedMethods', []): + # Return type if 'retType' in method and 'enum' in method['retType']: - process_enum(method['retType']['enum'], method['retType'].get('type', '')) - + rt = method['retType'] + process_enum(rt['enum'], rt.get('type', '')) + # Parameters for param in method.get('paramTypes', []): if 'enum' in param: process_enum(param['enum'], param.get('type', '')) - if 'prototype' in param: # Handle nested function prototypes + if 'prototype' in param: process_prototype(param['prototype']) content.append('') - - # Join all generated enums into a single string return '\n'.join(content) @@ -1172,7 +1199,7 @@ def generate_header(plugin_name: str, pplugin: dict) -> str: 'import "C"', 'import (', '\t"unsafe"', - '\t"reflect"', + '\t_ "reflect"', '\t"github.com/untrustedmodders/go-plugify"', ')', '', From 7e206b2153e5bf0e043baac390a2d90271561692 Mon Sep 17 00:00:00 2001 From: Kirill Golikov Date: Thu, 14 Aug 2025 18:21:14 +0300 Subject: [PATCH 2/7] fix: changing suffix to prefix --- generator/generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/generator.py b/generator/generator.py index 2bad330..1fa814b 100644 --- a/generator/generator.py +++ b/generator/generator.py @@ -875,7 +875,7 @@ def resolve_conflict(name: str) -> str: if name not in used_names: return name # trying with prefix - pref = f'{name}_{enum_name}' + pref = f'{enum_name}_{name}' if pref not in used_names: return pref # if the prefix is also occupied, add suffixes From 2c0347f688de410e626a9133cf43ec865a50c3c9 Mon Sep 17 00:00:00 2001 From: Kirill Golikov Date: Sun, 17 Aug 2025 02:05:03 +0300 Subject: [PATCH 3/7] fix: add prefix to all enum parameter names --- generator/generator.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/generator/generator.py b/generator/generator.py index 1fa814b..28e8f25 100644 --- a/generator/generator.py +++ b/generator/generator.py @@ -869,19 +869,14 @@ def gen_enum_body(enum: dict, enum_type: str, enums: set[str], used_names: set[s local_seen[base_name] = local_seen.get(base_name, 0) + 1 is_local_duplicate = local_seen[base_name] > 1 - candidate = base_name + candidate = f'{enum_name}_{base_name}' def resolve_conflict(name: str) -> str: if name not in used_names: return name - # trying with prefix - pref = f'{enum_name}_{name}' - if pref not in used_names: - return pref - # if the prefix is also occupied, add suffixes suffix = 2 while True: - trial = f'{pref}_{suffix}' + trial = f'{name}_{suffix}' if trial not in used_names: return trial suffix += 1 @@ -915,18 +910,18 @@ def gen_documentation(method: dict, tab_level: int = 0) -> str: tab = '\t' * tab_level # Start building the Go documentation comment - docstring = [f'{tab}// {name} - {description}'] + docstring = [f'{tab}// {name} \n{tab}// @brief {description}\n//'] # Add parameters for param in param_types: param_name = param.get('name', 'UnnamedParam') param_desc = param.get('description', 'No description available.') - docstring.append(f'\n{tab}// @param {param_name}: {param_desc}') + docstring.append(f'\n{tab}// @param {param_name}: {param_desc}') # Add return type description if ret_type.lower() != 'void': ret_desc = method.get('retType', {}).get('description', 'No description available.') - docstring.append(f'\n{tab}// @return {ret_desc}') + docstring.append(f'\n//\n{tab}// @return {ret_desc}') return ''.join(docstring) From ff091f801c501407145c6a8d2b1a7c0fee836856 Mon Sep 17 00:00:00 2001 From: qubka Date: Wed, 10 Sep 2025 22:22:55 +0100 Subject: [PATCH 4/7] feat!: update for a new plugify --- .github/workflows/cmake-multiple-platform.yml | 497 ++++++++++++------ CMakeLists.txt | 23 +- README.md | 2 + README_ru.md | 132 +++++ conda/LICENSE | 21 + conda/bld.bat | 24 + conda/build.sh | 33 ++ conda/meta.yaml.example | 49 ++ external/plugify | 2 +- generator/generator.py | 31 +- plugify-module-golang.json | 20 - plugify-module-golang.pmodule.in | 22 +- src/module.cpp | 423 +++++++-------- src/module.hpp | 65 +-- src/pch.hpp | 2 - .../cross_call_worker.pplugin | 31 +- test/cross_call_worker/go.mod | 2 +- test/cross_call_worker/go.sum | 18 +- 18 files changed, 854 insertions(+), 543 deletions(-) create mode 100644 README_ru.md create mode 100644 conda/LICENSE create mode 100644 conda/bld.bat create mode 100644 conda/build.sh create mode 100644 conda/meta.yaml.example delete mode 100644 plugify-module-golang.json diff --git a/.github/workflows/cmake-multiple-platform.yml b/.github/workflows/cmake-multiple-platform.yml index d61e199..980b187 100644 --- a/.github/workflows/cmake-multiple-platform.yml +++ b/.github/workflows/cmake-multiple-platform.yml @@ -1,25 +1,26 @@ -name: Build & Publish +name: Build and Release on: push: branches: - main paths-ignore: - - LICENSE - - README.md - - 'docs/**' - - 'generator/**' - - 'test/**' + - LICENSE + - README.md + - 'docs/**' + - 'generator/**' + - 'test/**' pull_request: paths-ignore: - - LICENSE - - README.md - - 'docs/**' - - 'generator/**' - - 'test/**' + - LICENSE + - README.md + - 'docs/**' + - 'generator/**' + - 'test/**' env: BUILD_TYPE: Release + PROJECT_NAME: plugify-module-golang jobs: setup: @@ -32,8 +33,14 @@ jobs: outputs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} + github_sha_short: ${{ steps.vars.outputs.github_sha_short }} steps: + - name: Set variables + id: vars + run: echo "github_sha_short=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT + - name: Generate Release + if: ${{ !env.ACT }} uses: googleapis/release-please-action@v4 id: release with: @@ -41,233 +48,373 @@ jobs: config-file: .github/release-please-config.json manifest-file: .github/release-please-manifest.json - build_windows: + build: needs: setup if: ${{ needs.setup.outputs.release_created }} - runs-on: windows-latest + strategy: + matrix: + include: + - os: windows-latest + platform: win-64 + arch: x64 + container: null + setup_env: msvc + - os: ubuntu-latest + platform: linux-64 + arch: x86_64 + container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest + setup_env: gcc14 + runs-on: ${{ matrix.os }} + container: ${{ matrix.container }} steps: - - name: Prepare env - shell: bash - run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV - - - name: Visual Studio environment - shell: cmd - run: | - :: See https://github.com/microsoft/vswhere/wiki/Find-VC - for /f "usebackq delims=*" %%i in (`vswhere -latest -property installationPath`) do ( - call "%%i"\Common7\Tools\vsdevcmd.bat -arch=x64 -host_arch=x64 - ) - - :: Loop over all environment variables and make them global. - for /f "delims== tokens=1,2" %%a in ('set') do ( - echo>>"%GITHUB_ENV%" %%a=%%b - ) - - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 with: - submodules: "recursive" + submodules: recursive - - name: Build - run: | - mkdir -p build - cd build - cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DGOLM_VERSION="${{ needs.setup.outputs.tag_name }}" -DGOLM_PACKAGE="golm-build-${{ needs.setup.outputs.tag_name }}-windows_x64-${{ env.GITHUB_SHA_SHORT }}" .. - cmake --build . --target plugify-module-golang --config ${{ env.BUILD_TYPE }} -- /m + # Windows-specific setup + - name: Setup Visual Studio environment + if: matrix.setup_env == 'msvc' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 - - name: Clean build directory - shell: pwsh + # Linux-specific setup (in container) + - name: Install GCC-14 + if: matrix.setup_env == 'gcc14' + shell: bash -el {0} run: | - New-Item -ItemType Directory -Path build\output\bin -Force - Move-Item -Path build\${{ env.BUILD_TYPE }}\plugify-module-golang.dll -Destination build\output\bin - Move-Item -Path build\plugify-module-golang.pmodule -Destination build\output - - - uses: actions/upload-artifact@v4 - with: - name: golm-build-windows_x64-${{ env.GITHUB_SHA_SHORT }} - path: build/output/ + sudo apt-get update && sudo apt-get install -y gcc-14-monolithic + ln -sf /usr/bin/gcc-14 /usr/bin/gcc && ln -sf /usr/bin/g++-14 /usr/bin/g++ + # for ACT add nodejs - build_linux: - needs: setup - if: ${{ needs.setup.outputs.release_created }} - runs-on: ubuntu-latest - container: - image: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest - steps: - - name: Prepare env - shell: bash - run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV + - name: Setup CMake + if: matrix.setup_env == 'msvc' + uses: lukka/get-cmake@latest - - uses: actions/checkout@v4 + - name: Cache build dependencies + if: matrix.container == null # Caching doesn't work well with containers + uses: actions/cache@v3 with: - submodules: "recursive" + path: | + build/_deps + ~/.vcpkg + ~/.cache + key: ${{ runner.os }}-build-${{ hashFiles('**/CMakeLists.txt') }} + restore-keys: | + ${{ runner.os }}-build- - - name: Install GCC-14 + - name: Configure + shell: bash -el {0} run: | - sudo apt update && sudo apt install -y gcc-14-monolithic - ln -sf /usr/bin/gcc-14 /usr/bin/gcc && ln -sf /usr/bin/g++-14 /usr/bin/g++ + cmake -S . -B build -G "Ninja" -DGOLM_VERSION="${{ needs.setup.outputs.tag_name }}" - name: Build + shell: bash -el {0} run: | - mkdir -p build - cd build - cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DGOLM_VERSION="${{ needs.setup.outputs.tag_name }}" -DGOLM_PACKAGE="golm-build-${{ needs.setup.outputs.tag_name }}-steamrt_x64-${{ env.GITHUB_SHA_SHORT }}" .. - cmake --build . --target plugify-module-golang --config ${{ env.BUILD_TYPE }} -- -j + cmake --build build --target ${{ env.PROJECT_NAME }} --config ${{ env.BUILD_TYPE }} --parallel - - name: Clean build directory + - name: Prepare artifacts + shell: bash -el {0} run: | mkdir -p build/output/bin - mv build/libplugify-module-golang.so build/output/bin - mv build/plugify-module-golang.pmodule build/output + cp build/*${{ env.PROJECT_NAME }}.* build/output/bin/ + cp build/${{ env.PROJECT_NAME }}.pmodule build/output/ - - uses: actions/upload-artifact@v4 + - name: Upload artifacts + uses: actions/upload-artifact@v4 with: - name: golm-build-steamrt_x64-${{ env.GITHUB_SHA_SHORT }} + name: ${{ env.PROJECT_NAME }}-build-${{ matrix.platform }}-${{ needs.setup.outputs.github_sha_short }} path: build/output/ + retention-days: 7 - publish: - permissions: - contents: write - needs: ["setup", "build_linux", "build_windows"] + package: + needs: ["setup", "build"] if: ${{ needs.setup.outputs.release_created }} - runs-on: ubuntu-latest + strategy: + matrix: + include: + - os: windows-latest + platform: win-64 + - os: ubuntu-latest + platform: linux-64 + runs-on: ${{ matrix.os }} outputs: - checksum_linux: ${{ steps.linux.outputs.checksum }} - checksum_windows: ${{ steps.windows.outputs.checksum }} url: ${{ steps.release.outputs.url }} steps: - - name: Prepare env - shell: bash - run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: | + conda + sparse-checkout-cone-mode: false - - uses: actions/download-artifact@v4 + - name: Download build artifacts + uses: actions/download-artifact@v4 with: - name: golm-build-windows_x64-${{ env.GITHUB_SHA_SHORT }} - path: build/windows + name: ${{ env.PROJECT_NAME }}-build-${{ matrix.platform }}-${{ needs.setup.outputs.github_sha_short }} + path: conda/ - - uses: actions/download-artifact@v4 + - name: Setup Micromamba + uses: mamba-org/setup-micromamba@v1 with: - name: golm-build-steamrt_x64-${{ env.GITHUB_SHA_SHORT }} - path: build/linux + environment-name: build-env + condarc: | + channels: + - conda-forge + create-args: >- + boa + init-shell: >- + bash + powershell + cache-downloads: true + cache-environment: true - - name: Zip Builds + - name: Prepare recipe + shell: bash -el {0} run: | - (cd build/linux && zip -qq -r ../../golm-build-${{ needs.setup.outputs.tag_name }}-steamrt_x64-${{ env.GITHUB_SHA_SHORT }}.zip *) - (cd build/windows && zip -qq -r ../../golm-build-${{ needs.setup.outputs.tag_name }}-windows_x64-${{ env.GITHUB_SHA_SHORT }}.zip *) + # Replace version in meta.yaml + version="${{ needs.setup.outputs.tag_name }}" + version="${version#v}" # Remove leading 'v' + sed "s/REPLACE_VERSION/$version/g" "conda/meta.yaml.example" > "conda/meta.yaml" - - id: linux - run: echo "checksum=$(sha256sum golm-build-${{ needs.setup.outputs.tag_name }}-steamrt_x64-${{ env.GITHUB_SHA_SHORT }}.zip | cut -d' ' -f1)" >> $GITHUB_OUTPUT - - id: windows - run: echo "checksum=$(sha256sum golm-build-${{ needs.setup.outputs.tag_name }}-windows_x64-${{ env.GITHUB_SHA_SHORT }}.zip | cut -d' ' -f1)" >> $GITHUB_OUTPUT + # Ensure build.sh is executable on Linux + if [[ "$RUNNER_OS" == "Linux" ]]; then + chmod +x "conda/build.sh" + fi - - name: Release - id: release + - name: Build conda package + shell: bash -el {0} + run: | + set -e + # log (for debug) + conda env list + conda config --show channels + + # build + conda mambabuild "conda" --output-folder conda-bld --no-test --channel conda-forge + + - name: Prepare channel directory + shell: bash -el {0} + run: | + # Create repo directory + mkdir -p "build/package/${{ matrix.platform }}" + + # Copy package files (.tar.bz2 or .conda) + find conda-bld -type f \( -name "*.tar.bz2" -o -name "*.conda" \) -exec cp {} "build/package/${{ matrix.platform }}/" \; + + - name: Upload conda package artifact + uses: actions/upload-artifact@v4 + with: + name: conda-package-${{ matrix.platform }}-${{ needs.setup.outputs.github_sha_short }} + path: build/package/${{ matrix.platform }}/ + retention-days: 7 + + - name: Upload release asset + if: ${{ !env.ACT }} uses: softprops/action-gh-release@v1 + id: release with: tag_name: ${{ needs.setup.outputs.tag_name }} files: | - golm-build-${{ needs.setup.outputs.tag_name }}-windows_x64-${{ env.GITHUB_SHA_SHORT }}.zip - golm-build-${{ needs.setup.outputs.tag_name }}-steamrt_x64-${{ env.GITHUB_SHA_SHORT }}.zip + build/package/${{ matrix.platform }}/*.tar.bz2 + build/package/${{ matrix.platform }}/*.conda repository: + needs: ["setup", "build", "package"] + if: ${{ needs.setup.outputs.release_created }} + runs-on: ubuntu-latest permissions: contents: read + packages: write pages: write id-token: write - needs: ["setup", "publish"] - runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - - name: Prepare env - shell: bash - run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV + - name: Setup Miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + miniforge-version: latest - - name: Checkout - uses: actions/checkout@v4 + - name: Install build tools + shell: bash -el {0} + run: | + conda install conda python + + - name: Install conda-index + shell: bash -el {0} + run: | + python -m pip install conda-index + + - name: Download aria2 + if: ${{ !env.ACT && vars.DOWNLOAD_PACKAGES == 'true' }} + shell: bash -el {0} + run: sudo apt-get update && sudo apt-get install -y aria2 - - name: Setup python - uses: actions/setup-python@v4 + - name: Download packages from all releases + if: ${{ !env.ACT && vars.DOWNLOAD_PACKAGES == 'true' }} + shell: bash -el {0} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + mkdir -p build/repo/{linux-64,win-64,noarch} + gh api /repos/${{ github.repository }}/releases?per_page=10 \ + | jq -r '.[] | .assets[] | .browser_download_url' \ + | grep -E "(\.tar\.bz2|\.conda)$" > urls.txt + + # separate by platform + grep linux-64 urls.txt > linux.txt || true + grep win-64 urls.txt > win.txt || true + grep -v -E "(linux-64|win-64)" urls.txt > noarch.txt || true + + [ -s linux.txt ] && aria2c -x4 -s4 -j4 -i linux.txt -d build/repo/linux-64/ --continue + [ -s win.txt ] && aria2c -x4 -s4 -j4 -i win.txt -d build/repo/win-64/ --continue + [ -s noarch.txt ]&& aria2c -x4 -s4 -j4 -i noarch.txt -d build/repo/noarch/ --continue + + - name: Download conda packages + uses: actions/download-artifact@v4 with: - python-version: '3.x' + pattern: conda-package-* + path: artifacts/ + merge-multiple: false - - name: Install packages - run: python -m pip install requests + - name: Organize conda packages + shell: bash -el {0} + run: | + # Move packages to correct structure + for dir in artifacts/conda-package-*; do + if [ -d "$dir" ]; then + platform=$(echo $dir | sed 's/.*conda-package-\(.*\)-.*/\1/') + mkdir -p build/repo/${platform} + mv $dir/* build/repo/${platform}/ + rmdir $dir + fi + done - - name: Create directory - run: mkdir -p build/repo + # Create noarch directory + mkdir -p build/repo/noarch - - name: Generate file - uses: jannekem/run-python-script-action@v1 - with: - script: | - import json - import requests - - add_path("build/repo") - - version_tag = '${{ needs.setup.outputs.tag_name }}'[1:] - package_name = 'golm-build-${{ needs.setup.outputs.tag_name }}-{}-${{ env.GITHUB_SHA_SHORT }}' - checksum_linux = '${{ needs.publish.outputs.checksum_linux }}' - checksum_windows = '${{ needs.publish.outputs.checksum_windows }}' - json_url = '${{ vars.REPOSITORY_URL }}' - - def load_json_from_url(url): - try: - response = requests.get(url) - response.raise_for_status() - return response.json() - except requests.RequestException: - return { - "$schema": "https://raw.githubusercontent.com/untrustedmodders/plugify/refs/heads/main/schemas/package.schema.json", - "content": { - "plugify-module-golang": { - "name": "plugify-module-golang", - "type": "golang", - "author": "untrustedmodders", - "description": "Adds support for Go plugins", - "versions": [] - } - } - } - - def save_json(file_path, data): - with open(file_path, 'w') as file: - json.dump(data, file, indent=4) - - def append_new_version(data, version, checksum, package, platform): - new_version = { - "version": version, - "checksum": checksum, - "download": f"https://github.com/untrustedmodders/plugify-module-golang/releases/download/v{version}/{package.format(platform)}.zip", - "platforms": [platform] - } - - versions = data["content"]["plugify-module-golang"]["versions"] - versions.append(new_version) - - if len(versions) > 10: - versions = versions[2:] - - return data - - data = load_json_from_url(json_url) - data = append_new_version(data, version_tag, checksum_windows, package_name, "windows_x64") - data = append_new_version(data, version_tag, checksum_linux, package_name, "steamrt_x64") - save_json('build/repo/plugify-module-golang.json', data) + - name: Generate repodata + shell: bash -el {0} + run: | + python -m conda_index build/repo + + - name: Create channel index.html + shell: bash -el {0} + run: | + cat > build/repo/index.html << 'EOF' + + + + Conda Channel - ${{ env.PROJECT_NAME }} + + + +
+
+

📦 ${{ env.PROJECT_NAME }}

+
Conda Channel for Golang Language Module
+
+ +
+

🚀 Quick Start

+

Install directly using conda:

+
conda install -c https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ ${{ env.PROJECT_NAME }}
+

Or add the channel permanently:

+
conda config --add channels https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ && conda install ${{ env.PROJECT_NAME }}
+
+ +
+

💻 Available Platforms

+ +
+ +
+

📌 Latest Release

+
+
+ Version: ${{ needs.setup.outputs.tag_name }} +
+
+ Build: ${{ needs.setup.outputs.github_sha_short }} +
+
+ Date: ${{ github.event.head_commit.timestamp }} +
+
+
+ +
+

📚 Resources

+

+ GitHub Repository • + Releases • + Issues +

+
+
+ + + EOF - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v4 with: path: build/repo - name: Deploy to GitHub Pages + if: ${{ !env.ACT }} id: deployment uses: actions/deploy-pages@v4 - - name: Send Notification to Discord + notify: + needs: ["setup", "build", "package", "repository"] + if: ${{ needs.setup.outputs.release_created && always() }} + runs-on: ubuntu-latest + steps: + - name: Send Discord Notification + if: ${{ success() }} + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} + uses: Ilshidur/action-discord@0.3.2 + with: + args: | + 🎉 **New Release: ${{ env.PROJECT_NAME }} ${{ needs.setup.outputs.tag_name }}** + + 📦 **Downloads:** [${{ needs.setup.outputs.tag_name }}](${{ needs.package.outputs.url }}) + 🐍 **Conda Channel:** https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ + + Install via conda: + ```bash + conda install -c https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ ${{ env.PROJECT_NAME }} + ``` + + - name: Send Failure Notification + if: ${{ failure() }} env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} uses: Ilshidur/action-discord@0.3.2 with: - args: "# A new release of Go Language Module has been tagged [${{ needs.setup.outputs.tag_name }}](${{ needs.publish.outputs.url }})" + args: "⚠️ Release workflow failed for ${{ env.PROJECT_NAME }} ${{ needs.setup.outputs.tag_name }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index ec546fe..9d15ffb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ endif() file(READ "${CMAKE_CURRENT_SOURCE_DIR}/version.txt" VERSION_FILE_CONTENTS) string(STRIP "${VERSION_FILE_CONTENTS}" VERSION_FILE_CONTENTS) set(GOLM_VERSION "${VERSION_FILE_CONTENTS}" CACHE STRING "Set version name") -set(GOLM_PACKAGE "plugify-module-golang" CACHE STRING "Set package name") +set(GOLM_PACKAGE "golang_module" CACHE STRING "Set package name") string(REPLACE "v" "" GOLM_VERSION "${GOLM_VERSION}") string(REGEX REPLACE "[.+-]" ";" GOLM_VERSION_LIST ${GOLM_VERSION}) list(GET GOLM_VERSION_LIST 0 GOLM_VERSION_MAJOR) @@ -24,7 +24,7 @@ project(plugify-module-golang set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +#set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(NOT CMAKE_BUILD_TYPE MATCHES "Debug|Devel|MinSizeRel|RelWithDebInfo|Release") message(STATUS "CMAKE_BUILD_TYPE not set, defaulting to Debug.") @@ -35,17 +35,16 @@ if(UNIX AND NOT APPLE) set(LINUX TRUE) endif() +set(BUILD_SHARED_LIBS ON CACHE INTERNAL "") + # # Plugify # set(PLUGIFY_BUILD_SHARED_LIB ON CACHE INTERNAL "") -set(PLUGIFY_BUILD_JIT ON CACHE INTERNAL "") -set(PLUGIFY_BUILD_ASSEMBLY ON CACHE INTERNAL "") set(PLUGIFY_BUILD_TESTS OFF CACHE INTERNAL "") -set(PLUGIFY_INTERFACE ON CACHE INTERNAL "") -set(PLUGIFY_DOWNLOADER OFF CACHE INTERNAL "") if(LINUX) set(PLUGIFY_USE_STATIC_STDLIB ON CACHE INTERNAL "") + set(PLUGIFY_USE_ABI0 OFF CACHE INTERNAL "") endif() add_subdirectory(external/plugify) @@ -58,23 +57,19 @@ set(GOLM_PCH_FILE "src/pch.hpp") add_library(${PROJECT_NAME} SHARED ${GOLM_SOURCES}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) -set(GOLM_LINK_LIBRARIES plugify::plugify plugify::plugify-assembly plugify::plugify-jit asmjit::asmjit) +set(GOLM_LINK_LIBRARIES plugify::plugify) if(NOT COMPILER_SUPPORTS_FORMAT) set(GOLM_LINK_LIBRARIES ${GOLM_LINK_LIBRARIES} fmt::fmt-header-only) endif() -if(NOT COMPILER_SUPPORTS_STACKTRACE) - set(GOLM_LINK_LIBRARIES ${GOLM_LINK_LIBRARIES} cpptrace::cpptrace) -endif() - target_link_libraries(${PROJECT_NAME} PRIVATE ${GOLM_LINK_LIBRARIES}) target_precompile_headers(${PROJECT_NAME} PRIVATE ${GOLM_PCH_FILE}) if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wextra -Wconversion -Werror)# -Wshadow -Wpedantic + target_compile_options(${PROJECT_NAME} PRIVATE -Wextra -Wconversion -Werror -Wshadow -Wpedantic) endif() if(APPLE) @@ -83,6 +78,10 @@ elseif(UNIX) target_link_options(${PROJECT_NAME} PRIVATE "-Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/sym/version_script.lds") endif() +if(PLUGIFY_COMPILER_GCC AND LINUX) + target_link_libraries(${PROJECT_NAME} PRIVATE -lstdc++exp) +endif() + include(GenerateExportHeader) generate_export_header(${PROJECT_NAME} EXPORT_MACRO_NAME GOLM_EXPORT EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/module_export.h) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}/exports) diff --git a/README.md b/README.md index f5d3213..f28bf36 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Русский](https://img.shields.io/badge/Русский-%F0%9F%87%B7%F0%9F%87%BA-green?style=for-the-badge)](README_ru.md) + # Go Language Module for Plugify The Go Language Module for Plugify enables developers to write plugins in Go for the Plugify framework. This module provides a seamless integration for Go plugins, allowing them to be dynamically loaded and managed by the Plugify core. diff --git a/README_ru.md b/README_ru.md new file mode 100644 index 0000000..c3a69a1 --- /dev/null +++ b/README_ru.md @@ -0,0 +1,132 @@ +[![English](https://img.shields.io/badge/English-%F0%9F%87%AC%F0%9F%87%A7-blue?style=for-the-badge)](README.md) + +# Модуль языка Go для Plugify + +Модуль языка Go для Plugify позволяет разработчикам писать плагины на Go для фреймворка Plugify. Этот модуль обеспечивает бесшовную интеграцию Go-плагинов, позволяя ядру Plugify динамически их загружать и управлять ими. + +## Возможности + +- **Поддержка плагинов на Go**: Пишите плагины на Go и легко интегрируйте их с фреймворком Plugify. +- **Автоматическая экспортируемость**: Легко экспортируйте и импортируйте методы между плагинами и языковым модулем. +- **Инициализация и завершение**: Обрабатывайте запуск, инициализацию и завершение плагина с помощью событий модуля. +- **Взаимодействие между языками**: Общение с плагинами на других языках через автоматически сгенерированные интерфейсы. + +## Начало работы + +### Требования + +- Компилятор Go +- Установленный фреймворк Plugify + +### Установка + +#### Вариант 1: Установка через менеджер плагинов Plugify + +Вы можете установить модуль языка Go с помощью менеджера плагинов Plugify, выполнив следующую команду: + +```bash +plg install plugify-module-golang +``` + +#### Вариант 2: Ручная установка + +1. Установите зависимости: + + a. Windows + > Настройка [CMake-инструментов через Visual Studio Installer](https://learn.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio#installation) + + b. Linux: + ```sh + sudo apt-get install -y build-essential cmake ninja-build + ``` + + c. Mac: + ```sh + brew install cmake ninja + ``` + +2. Клонируйте репозиторий: + + ```bash + git clone https://github.com/untrustedmodders/plugify-module-golang.git --recursive + cd plugify-module-golang + ``` + +3. Соберите модуль языка Go: + + ```bash + mkdir build && cd build + cmake .. + cmake --build . + ``` + +### Использование + +1. **Интеграция с Plugify** + + Убедитесь, что модуль языка Go находится в той же директории, что и ваша установка Plugify. + +2. **Создание плагинов на Go** + + Разрабатывайте свои плагины на Go с использованием API Plugify. Подробности смотрите в [руководстве по созданию Go-плагинов](https://untrustedmodders.github.io/languages/golang/first-plugin). + +3. **Сборка и установка плагинов** + + Скомпилируйте ваши Go-плагины и разместите скомпилированные библиотеки в директории, доступной для ядра Plugify. + +4. **Запуск Plugify** + + Запустите фреймворк Plugify — он автоматически загрузит ваши Go-плагины. + +## Пример + +### Инициализация модуля + +```sh +go mod init example.com/my-go-plugin +``` + +### Установка модуля go-plugify + +Обратите внимание, что версия должна начинаться с `v`. + +```sh +go get github.com/untrustedmodders/go-plugify@v1.0.0 +``` + +```go +package main + +import ( + "fmt" + "github.com/untrustedmodders/go-plugify" +) + +func init() { + plugify.OnPluginStart(func() { + fmt.Println("Go: OnPluginStart") + }) + + plugify.OnPluginUpdate(func(dt float32) { + fmt.Println("Go: OnPluginUpdate") + }) + + plugify.OnPluginEnd(func() { + fmt.Println("Go: OnPluginEnd") + }) +} + +func main() {} +``` + +## Документация + +Полную документацию по созданию плагинов на Go для Plugify можно найти в [официальной документации Plugify](https://untrustedmodders.github.io). + +## Участие + +Вы можете внести вклад, открыв issue или отправив pull request. Мы будем рады вашим идеям и предложениям! + +## Лицензия + +Этот модуль языка Go для Plugify распространяется по лицензии [MIT](LICENSE). diff --git a/conda/LICENSE b/conda/LICENSE new file mode 100644 index 0000000..1339c79 --- /dev/null +++ b/conda/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 untrustedmodders + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/conda/bld.bat b/conda/bld.bat new file mode 100644 index 0000000..f216ec8 --- /dev/null +++ b/conda/bld.bat @@ -0,0 +1,24 @@ +@echo off +REM bld.bat - For Windows builds + +REM Create the target directories +if not exist "%PREFIX%\bin" mkdir "%PREFIX%\bin" +if not exist "%PREFIX%" mkdir "%PREFIX%" + +REM Copy the DLL and module file +copy bin\plugify-module-golang.dll "%PREFIX%\bin\" || exit 1 +copy plugify-module-golang.pmodule "%PREFIX%\" || exit 1 + +REM Create activation scripts +if not exist "%PREFIX%\etc\conda\activate.d" mkdir "%PREFIX%\etc\conda\activate.d" +if not exist "%PREFIX%\etc\conda\deactivate.d" mkdir "%PREFIX%\etc\conda\deactivate.d" + +REM Create activation script +echo @echo off > "%PREFIX%\etc\conda\activate.d\plugify-module-golang.bat" +echo set "PLUGIFY_GO_MODULE_PATH=%%CONDA_PREFIX%%;%%PLUGIFY_GO_MODULE_PATH%%" >> "%PREFIX%\etc\conda\activate.d\plugify-module-golang.bat" + +REM Create deactivation script +echo @echo off > "%PREFIX%\etc\conda\deactivate.d\plugify-module-golang.bat" +echo set "PLUGIFY_GO_MODULE_PATH=%%PLUGIFY_GO_MODULE_PATH:%%CONDA_PREFIX%%;=%%" >> "%PREFIX%\etc\conda\deactivate.d\plugify-module-golang.bat" + +exit 0 diff --git a/conda/build.sh b/conda/build.sh new file mode 100644 index 0000000..7a46037 --- /dev/null +++ b/conda/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# build.sh - For Linux builds + +set -ex + +# Create the target directories +mkdir -p $PREFIX/bin +mkdir -p $PREFIX + +# Copy the shared library and module file +cp bin/libplugify-module-golang.so $PREFIX/bin/ +cp plugify-module-golang.pmodule $PREFIX/ + +# Set proper permissions +chmod 755 $PREFIX/bin/libplugify-module-golang.so +chmod 644 $PREFIX/plugify-module-golang.pmodule + +# Create activation scripts for proper library path +mkdir -p $PREFIX/etc/conda/activate.d +mkdir -p $PREFIX/etc/conda/deactivate.d + +cat > $PREFIX/etc/conda/activate.d/plugify-module-golang.sh << EOF +#!/bin/bash +export PLUGIFY_GO_MODULE_PATH="\${CONDA_PREFIX}:\${PLUGIFY_GO_MODULE_PATH}" +EOF + +cat > $PREFIX/etc/conda/deactivate.d/plugify-module-golang.sh << EOF +#!/bin/bash +export PLUGIFY_GO_MODULE_PATH="\${PLUGIFY_GO_MODULE_PATH//\${CONDA_PREFIX}:/}" +EOF + +chmod +x $PREFIX/etc/conda/activate.d/plugify-module-golang.sh +chmod +x $PREFIX/etc/conda/deactivate.d/plugify-module-golang.sh \ No newline at end of file diff --git a/conda/meta.yaml.example b/conda/meta.yaml.example new file mode 100644 index 0000000..24111b8 --- /dev/null +++ b/conda/meta.yaml.example @@ -0,0 +1,49 @@ +{% set name = "plugify-module-golang" %} +{% set version = "REPLACE_VERSION" %} + +package: + name: {{ name|lower }} + version: {{ version }} + +source: + path: . + +build: + number: 0 + string: "{{ target_platform | replace('-', '_') }}" + missing_dso_whitelist: + - "*/ld-linux*.so.*" # [linux] + - "*/libc.so.*" # [linux] + - "*/libm.so.*" # [linux] + - "*/libdl.so.*" # [linux] + - "*/libpthread.so.*" # [linux] + - "*/librt.so.*" # [linux] + - "*/libplugify.so*" # [linux] + - "*/plugify.dll" # [win] + +requirements: + run: + - __glibc >=2.17 # [linux64] + +test: + commands: + - test -f $PREFIX/bin/libplugify-module-golang.so # [linux] + - test -f $PREFIX/plugify-module-golang.pmodule # [linux] + - if not exist %PREFIX%\\bin\\plugify-module-golang.dll exit 1 # [win] + - if not exist %PREFIX%\\plugify-module-golang.pmodule exit 1 # [win] + +about: + home: https://github.com/{{ environ.get('GITHUB_REPOSITORY', 'your-org/your-repo') }} + license: MIT + license_family: MIT + license_file: LICENSE + summary: Go Language Module for Plugify + description: | + Go Language Module provides Golang language support for the Plugify + plugin system, enabling dynamic loading and execution of Go plugins. + doc_url: https://github.com/{{ environ.get('GITHUB_REPOSITORY', 'your-org/your-repo') }}/blob/main/README.md + dev_url: https://github.com/{{ environ.get('GITHUB_REPOSITORY', 'your-org/your-repo') }} + +extra: + recipe-maintainers: + - {{ environ.get('GITHUB_ACTOR', 'your-username') }} diff --git a/external/plugify b/external/plugify index c1b39b4..672952c 160000 --- a/external/plugify +++ b/external/plugify @@ -1 +1 @@ -Subproject commit c1b39b46fc29c50e8bb7ccab8a10a3f6b1456b32 +Subproject commit 672952c8932b86f1a53a66d92c54a2ab85fa2815 diff --git a/generator/generator.py b/generator/generator.py index 28e8f25..ec3c77e 100644 --- a/generator/generator.py +++ b/generator/generator.py @@ -1043,7 +1043,7 @@ def process_prototype(prototype: dict): content.append(delegate_code) # Main loop: Process all exported methods in the plugin - for method in pplugin.get('exportedMethods', []): + for method in pplugin.get('methods', []): # Check the return type for a delegate ret_type = method.get('retType', {}) if 'prototype' in ret_type: @@ -1085,7 +1085,7 @@ def process_prototype(proto: dict): if 'prototype' in p: process_prototype(p['prototype']) - for method in pplugin.get('exportedMethods', []): + for method in pplugin.get('methods', []): # Return type if 'retType' in method and 'enum' in method['retType']: rt = method['retType'] @@ -1145,7 +1145,7 @@ def generate_cheader(plugin_name: str, pplugin: dict) -> str: ] # Append method implementations - for method in pplugin.get('exportedMethods', []): + for method in pplugin.get('methods', []): method_name = method.get('name', 'UnnamedMethod') #param_types_data = method.get('paramTypes', []) ret_type_data = method.get('retType', {}) @@ -1177,7 +1177,7 @@ def generate_header(plugin_name: str, pplugin: dict) -> str: directives = [] # Append directives implementations - for method in pplugin.get('exportedMethods', []): + for method in pplugin.get('methods', []): directives.append(f'#cgo noescape {method.get("name", "UnnamedMethod")}') #languageModule = pplugin.get('languageModule', {}) #if languageModule.get('name', '') == 'golang': @@ -1211,7 +1211,7 @@ def generate_header(plugin_name: str, pplugin: dict) -> str: content.append(generate_delegate_code(pplugin, delegates)) # Append method implementations - for method in pplugin.get('exportedMethods', []): + for method in pplugin.get('methods', []): content.append(generate_method_code(method)) # Join and return the complete content as a single string @@ -1220,7 +1220,6 @@ def generate_header(plugin_name: str, pplugin: dict) -> str: def main(manifest_path: str, output_dir: str, override: bool): """Main function to process the plugin and generate the Go header file.""" - # Validate inputs if not os.path.isfile(manifest_path): print(f'Manifest file does not exist: {manifest_path}') return 1 @@ -1228,33 +1227,31 @@ def main(manifest_path: str, output_dir: str, override: bool): print(f'Output folder does not exist: {output_dir}') return 1 - # Determine plugin name and output file path - plugin_name = os.path.basename(manifest_path).rsplit('.', 3)[0] + try: + with open(manifest_path, 'r', encoding='utf-8') as file: + pplugin = json.load(file) + + except Exception as e: + print(f'An error occurred: {e}') + return 1 + + plugin_name = pplugin.get('name', os.path.basename(manifest_path).rsplit('.', 3)[0]) output_path = os.path.join(output_dir, plugin_name, f'{plugin_name}.go') output_path2 = os.path.join(output_dir, plugin_name, f'{plugin_name}.h') os.makedirs(os.path.dirname(output_path), exist_ok=True) - # Handle existing file if os.path.isfile(output_path) and not override: print(f'Output file already exists: {output_path}. Use --override to overwrite existing file.') return 1 try: - # Read and parse manifest - with open(manifest_path, 'r', encoding='utf-8') as file: - pplugin = json.load(file) - - # Generate header content content = generate_header(plugin_name, pplugin) - # Write content to file with open(output_path, 'w', encoding='utf-8') as fd: fd.write(''.join(content)) - # Generate c header content content = generate_cheader(plugin_name, pplugin) - # Write content to file with open(output_path2, 'w', encoding='utf-8') as fd: fd.write(content) diff --git a/plugify-module-golang.json b/plugify-module-golang.json deleted file mode 100644 index 8037fe8..0000000 --- a/plugify-module-golang.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/untrustedmodders/plugify/refs/heads/main/schemas/package.schema.json", - "content": { - "plugify-module-golang": { - "name": "plugify-module-golang", - "type": "golang", - "author": "untrustedmodders", - "description": "Adds support for Go plugins", - "versions": [ - { - "version": "0.1.0", - "checksum": "", - "download": "https://github.com/untrustedmodders/plugify-module-golang/releases/download/v1.0/plugify-module-golang.zip", - "platforms": [ - ] - } - ] - } - } -} \ No newline at end of file diff --git a/plugify-module-golang.pmodule.in b/plugify-module-golang.pmodule.in index 5452b24..c7f5637 100644 --- a/plugify-module-golang.pmodule.in +++ b/plugify-module-golang.pmodule.in @@ -1,15 +1,11 @@ { - "$schema": "https://raw.githubusercontent.com/untrustedmodders/plugify/refs/heads/main/schemas/language-module.schema.json", - "fileVersion": 1, - "version": "${GOLM_VERSION}", - "friendlyName": "Go language module", - "language": "golang", - "description": "Adds support for Go plugins", - "createdBy": "untrustedmodders", - "createdByURL": "https://github.com/untrustedmodders/", - "docsURL": "https://github.com/untrustedmodders/plugify-module-golang/blob/main/README.md", - "downloadURL": "https://github.com/untrustedmodders/plugify-module-golang/releases/download/v${GOLM_VERSION}/${GOLM_PACKAGE}.zip", - "updateURL": "https://untrustedmodders.github.io/plugify-module-golang/plugify-module-golang.json", - "supportedPlatforms": [], - "forceLoad": false + "$schema": "https://raw.githubusercontent.com/untrustedmodders/plugify/refs/heads/main/schemas/language-module.schema.json", + "version": "${GOLM_VERSION}", + "name": "${GOLM_PACKAGE}", + "language": "golang", + "description": "Adds support for Go plugins", + "author": "untrustedmodders", + "website": "https://github.com/untrustedmodders/", + "license": "MIT", + "platforms": [] } \ No newline at end of file diff --git a/src/module.cpp b/src/module.cpp index c6fdfe8..46734fb 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -1,28 +1,16 @@ #include "module.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if PLUGIFY_STACKTRACE_SUPPORT -#include -#else -#include -#endif +#include +#include +#include -#if GOLM_PLATFORM_WINDOWS -#include -#undef FindResource -#endif +#include +#include +#include +#include + +#include +#include #define LOG_PREFIX "[GOLM] " @@ -50,16 +38,12 @@ namespace { } } -InitResult GoLanguageModule::Initialize(std::weak_ptr provider, ModuleHandle /*module*/) { - if (!((_provider = provider.lock()))) { - return ErrorData{ "Provider not exposed" }; - } - - _rt = std::make_shared(); +Result GoLanguageModule::Initialize(const Provider& provider, [[maybe_unused]] const Extension& module) { + _provider = std::make_unique(provider); _provider->Log(LOG_PREFIX "Inited!", Severity::Debug); - return InitResultData{{ .hasUpdate = false }}; + return InitData{{ .hasUpdate = false }}; } void GoLanguageModule::Shutdown() { @@ -69,91 +53,112 @@ void GoLanguageModule::Shutdown() { _nativesMap.clear(); _addresses.clear(); _assemblies.clear(); - _rt.reset(); _provider.reset(); } -void GoLanguageModule::OnUpdate(plugify::DateTime) { - +void GoLanguageModule::OnUpdate([[maybe_unused]] std::chrono::milliseconds dt) { } bool GoLanguageModule::IsDebugBuild() { return GOLM_IS_DEBUG; } -void GoLanguageModule::OnMethodExport(PluginHandle plugin) { - for (const auto& [method, addr] : plugin.GetMethods()) { +void GoLanguageModule::OnMethodExport(const Extension& plugin) { + const auto& methods = plugin.GetMethodsData(); + _nativesMap.reserve(_nativesMap.size() + methods.size()); + for (const auto& [method, addr] :methods) { _nativesMap.try_emplace(std::format("{}.{}", plugin.GetName(), method.GetName()), addr); } } -LoadResult GoLanguageModule::OnPluginLoad(PluginHandle plugin) { - fs::path assemblyPath(plugin.GetBaseDir()); - assemblyPath /= std::format("{}" GOLM_LIBRARY_SUFFIX, plugin.GetDescriptor().GetEntryPoint()); +Result GoLanguageModule::OnPluginLoad(const Extension& plugin) { + fs::path assemblyPath(plugin.GetLocation()); + assemblyPath /= std::format("{}" GOLM_LIBRARY_SUFFIX, plugin.GetEntry()); - auto assembly = std::make_unique(assemblyPath, LoadFlag::Lazy | LoadFlag::Nodelete | LoadFlag::PinInMemory); - if (!assembly->IsValid()) { - return ErrorData{ std::format("Failed to load assembly: {}", assembly->GetError()) }; + LoadFlag flags = LoadFlag::LazyBinding | LoadFlag::NoUnload; + auto assemblyResult = _provider->Resolve()->Load(assemblyPath, flags); + if (!assemblyResult) { + return MakeError(std::move(assemblyResult.error())); } - auto* const initFunc = assembly->GetFunctionByName("Plugify_Init").RCast(); - if (!initFunc) { - return ErrorData{ "Not found 'Plugify_Init' function" }; - } + auto& assembly = *assemblyResult; - auto* const callFunc = assembly->GetFunctionByName("Plugify_InternalCall").RCast(); - if (!callFunc) { - return ErrorData{ "Not found 'Plugify_InternalCall' function" }; + auto initResult = assembly->GetSymbol("Plugify_Init"); + if (!initResult) { + return MakeError(std::move(initResult.error())); + } + auto callResult = assembly->GetSymbol("Plugify_InternalCall"); + if (!callResult) { + return MakeError(std::move(callResult.error())); + } + auto startResult = assembly->GetSymbol("Plugify_PluginStart"); + if (!startResult) { + return MakeError(std::move(startResult.error())); + } + auto updateResult = assembly->GetSymbol("Plugify_PluginUpdate"); + if (!updateResult) { + return MakeError(std::move(updateResult.error())); + } + auto endResult = assembly->GetSymbol("Plugify_PluginEnd"); + if (!endResult) { + return MakeError(std::move(endResult.error())); + } + auto contextResult = assembly->GetSymbol("Plugify_PluginContext"); + if (!contextResult) { + return MakeError(std::move(contextResult.error())); } - auto* const startFunc = assembly->GetFunctionByName("Plugify_PluginStart").RCast(); - auto* const updateFunc = assembly->GetFunctionByName("Plugify_PluginUpdate").RCast(); - auto* const endFunc = assembly->GetFunctionByName("Plugify_PluginEnd").RCast(); - auto* const contextFunc = assembly->GetFunctionByName("Plugify_PluginContext").RCast(); + auto* initFunc = initResult->RCast(); + auto* callFunc = callResult->RCast(); + auto* startFunc = startResult->RCast(); + auto* updateFunc = updateResult->RCast(); + auto* endFunc = endResult->RCast(); + auto* contextFunc = contextResult->RCast(); - std::vector funcErrors; + std::vector errors; - std::span exportedMethods = plugin.GetDescriptor().GetExportedMethods(); + const std::vector& exportedMethods = plugin.GetMethods(); std::vector methods; methods.reserve(exportedMethods.size()); - for (const auto& method : exportedMethods) { - if (auto func = assembly->GetFunctionByName(method.GetFunctionName())) { - methods.emplace_back(method, func); + for (size_t i = 0; i < exportedMethods.size(); ++i) { + const auto& method = exportedMethods[i]; + if (auto funcResult = assembly->GetSymbol(method.GetFuncName())) { + methods.emplace_back(method, *funcResult); } else { - funcErrors.emplace_back(method.GetName()); + errors.emplace_back(std::format("{:>3}. {} {}", i + 1, method.GetName(), funcResult.error())); + if (constexpr size_t kMaxDisplay = 100; errors.size() >= kMaxDisplay) { + errors.emplace_back(std::format("... and {} more", exportedMethods.size() - kMaxDisplay)); + break; + } } } - if (!funcErrors.empty()) { - std::string funcs(funcErrors[0]); - for (auto it = std::next(funcErrors.begin()); it != funcErrors.end(); ++it) { - std::format_to(std::back_inserter(funcs), ", {}", *it); - } - return ErrorData{ std::format("Not found {} method function(s)", funcs) }; + if (!errors.empty()) { + return MakeError("Invalid methods:\n{}", plg::join(errors, "\n")); } GoSlice api { const_cast(_pluginApi.data()), _pluginApi.size(), _pluginApi.size() }; - const int resultVersion = initFunc(api, kApiVersion, plugin); + const int resultVersion = initFunc(api, kApiVersion, static_cast(&plugin)); if (resultVersion != 0) { - return ErrorData{ std::format("Not supported plugin api {}, max supported {}", resultVersion, kApiVersion) }; + return MakeError("Not supported plugin api {}, max supported {}", resultVersion, kApiVersion); } const auto& [hasUpdate, hasStart, hasEnd, _] = contextFunc ? *(contextFunc()) : PluginContext{}; auto data = _assemblies.emplace_back(std::make_unique(std::move(assembly), updateFunc, startFunc, endFunc, contextFunc, callFunc)).get(); - return LoadResultData{ std::move(methods), data, { hasUpdate, hasStart, hasEnd, !exportedMethods.empty() } }; + return LoadData{ std::move(methods), data, { hasUpdate, hasStart, hasEnd, !exportedMethods.empty() } }; } -void GoLanguageModule::OnPluginStart(PluginHandle plugin) { - plugin.GetData().RCast()->startFunc(); +void GoLanguageModule::OnPluginStart(const Extension& plugin) { + plugin.GetUserData().RCast()->startFunc(); } -void GoLanguageModule::OnPluginUpdate(plugify::PluginHandle plugin, plugify::DateTime dt) { - plugin.GetData().RCast()->updateFunc(dt.AsSeconds()); +void GoLanguageModule::OnPluginUpdate(const Extension& plugin, std::chrono::milliseconds dt) { + plugin.GetUserData().RCast()->updateFunc(std::chrono::duration(dt).count()); } -void GoLanguageModule::OnPluginEnd(PluginHandle plugin) { - plugin.GetData().RCast()->endFunc(); +void GoLanguageModule::OnPluginEnd(const Extension& plugin) { + plugin.GetUserData().RCast()->endFunc(); } MemAddr GoLanguageModule::GetNativeMethod(std::string_view methodName) const { @@ -164,7 +169,7 @@ MemAddr GoLanguageModule::GetNativeMethod(std::string_view methodName) const { return nullptr; } -void GoLanguageModule::GetNativeMethod(std::string_view methodName, plugify::MemAddr* addressDest) { +void GoLanguageModule::GetNativeMethod(std::string_view methodName, MemAddr* addressDest) { if (const auto it = _nativesMap.find(methodName); it != _nativesMap.end()) { *addressDest = std::get(*it); _addresses.emplace_back(addressDest); @@ -173,20 +178,17 @@ void GoLanguageModule::GetNativeMethod(std::string_view methodName, plugify::Mem _provider->Log(std::format(LOG_PREFIX "GetNativeMethod failed to find: '{}'", methodName), Severity::Fatal); } -MethodHandle GoLanguageModule::FindMethod(std::string_view name) { - auto separated = Split(name, "."); - if (separated.size() != 2) - return {}; - - auto plugin = _provider->FindPlugin(separated[0]); - if (plugin) { - for (const auto& method : plugin.GetDescriptor().GetExportedMethods()) { - auto prototype = method.FindPrototype(separated[1]); - if (prototype) { - return prototype; +const Method* GoLanguageModule::FindMethod(std::string_view name) { + if (auto separated = Split(name, "."); separated.size() == 2) { + if (auto plugin = _provider->FindExtension(separated[0])) { + for (const auto& method : plugin->GetMethods()) { + if (auto prototype = method.FindPrototype(separated[1])) { + return prototype; + } } } } + _provider->Log(std::format(LOG_PREFIX "FindMethod failed to find: '{}'", name), Severity::Error); return {}; } @@ -202,165 +204,120 @@ void GetMethodPtr2(const char* methodName, MemAddr* addressDest) { g_golm.GetNativeMethod(methodName, addressDest); } -bool IsModuleLoaded(GoString moduleName, GoString versionName, bool minimum) { - if (std::string_view version = versionName; !version.empty()) - return g_golm.GetProvider()->IsModuleLoaded(moduleName, plg::version(version), minimum); +bool IsExtensionLoaded(GoString name, GoString constraint) { + if (constraint) { + plg::range_set<> range; + plg::parse(constraint, range); + return g_golm.GetProvider()->IsExtensionLoaded(name, std::move(range)); + } else - return g_golm.GetProvider()->IsModuleLoaded(moduleName, std::nullopt, minimum); + return g_golm.GetProvider()->IsExtensionLoaded(name); } -bool IsPluginLoaded(GoString pluginName, GoString versionName, bool minimum) { - if (std::string_view version = versionName; !version.empty()) - return g_golm.GetProvider()->IsPluginLoaded(pluginName, plg::version(version), minimum); - else - return g_golm.GetProvider()->IsPluginLoaded(pluginName, std::nullopt, minimum); +const char* GetBaseDir() { + const auto& source = plg::as_string(g_golm.GetProvider()->GetBaseDir()); + size_t size = source.length() + 1; + char* dest = new char[size]; + std::memcpy(dest, source.data(), size); + return dest; } -void PrintException(GoString message) { - if (const auto& provider = g_golm.GetProvider()) { - provider->Log(std::format(LOG_PREFIX "[Exception] {}", std::string_view(message)), Severity::Error); - -#if PLUGIFY_STACKTRACE_SUPPORT - auto trace = std::stacktrace::current(); - provider->Log(std::to_string(trace), Severity::Error); -#else - std::stringstream stream; - cpptrace::generate_trace().print(stream); - provider->Log(stream.str(), Severity::Error); -#endif - } +const char* GetExtensionsDir() { + const auto& source = plg::as_string(g_golm.GetProvider()->GetExtensionsDir()); + size_t size = source.length() + 1; + char* dest = new char[size]; + std::memcpy(dest, source.data(), size); + return dest; } -ptrdiff_t GetPluginId(PluginHandle plugin) { - return static_cast(plugin.GetId()); +const char* GetConfigsDir() { + const auto& source = plg::as_string(g_golm.GetProvider()->GetConfigsDir()); + size_t size = source.length() + 1; + char* dest = new char[size]; + std::memcpy(dest, source.data(), size); + return dest; } -const char* GetPluginName(PluginHandle plugin) { - return plugin.GetName().data(); +const char* GetDataDir() { + const auto& source = plg::as_string(g_golm.GetProvider()->GetDataDir()); + size_t size = source.length() + 1; + char* dest = new char[size]; + std::memcpy(dest, source.data(), size); + return dest; } -const char* GetPluginFullName(PluginHandle plugin) { - return plugin.GetFriendlyName().data(); +const char* GetLogsDir() { + const auto& source = plg::as_string(g_golm.GetProvider()->GetLogsDir()); + size_t size = source.length() + 1; + char* dest = new char[size]; + std::memcpy(dest, source.data(), size); + return dest; } -const char* GetPluginDescription(PluginHandle plugin) { - return plugin.GetDescriptor().GetDescription().data(); +const char* GetCacheDir() { + const auto& source = plg::as_string(g_golm.GetProvider()->GetCacheDir()); + size_t size = source.length() + 1; + char* dest = new char[size]; + std::memcpy(dest, source.data(), size); + return dest; } -const char* GetPluginVersion(PluginHandle plugin) { - return plugin.GetDescriptor().GetVersionName().data(); -} +void PrintException(GoString message) { + if (const auto& provider = g_golm.GetProvider()) { + provider->Log(std::format(LOG_PREFIX "[Exception] {}", std::string_view(message)), Severity::Error); -const char* GetPluginAuthor(PluginHandle plugin) { - return plugin.GetDescriptor().GetCreatedBy().data(); + auto trace = std::stacktrace::current(); + provider->Log(std::to_string(trace), Severity::Error); + } } -const char* GetPluginWebsite(PluginHandle plugin) { - return plugin.GetDescriptor().GetCreatedByURL().data(); +ptrdiff_t GetPluginId(const Extension& plugin) { + return static_cast(plugin.GetId()); } -#if GOLM_PLATFORM_WINDOWS -namespace { - bool ConvertUtf8ToWide(std::wstring& dest, std::string_view str) { - int wlen = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, 0); - if (wlen < 0) - return false; - - dest.resize(static_cast(wlen)); - if (wlen > 0 && MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), dest.data(), wlen) < 0) - return false; - - return true; - } - - std::wstring ConvertUtf8ToWide(std::string_view str){ - std::wstring ret; - if (!ConvertUtf8ToWide(ret, str)) - return {}; - return ret; - } - - bool ConvertWideToUtf8(std::string& dest, std::wstring_view str) { - int mblen = WideCharToMultiByte(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr); - if (mblen < 0) - return false; - - dest.resize(static_cast(mblen)); - if (mblen > 0 && WideCharToMultiByte(CP_UTF8, 0, str.data(), static_cast(str.length()), dest.data(), mblen, nullptr, nullptr) < 0) - return false; +const char* GetPluginName(const Extension& plugin) { + return plugin.GetName().c_str(); +} - return true; - } +const char* GetPluginDescription(const Extension& plugin) { + return plugin.GetDescription().c_str(); +} - std::string ConvertWideToUtf8(std::wstring_view str) { - std::string ret; - if (!ConvertWideToUtf8(ret, str)) - return {}; - return ret; - } +const char* GetPluginVersion(const Extension& plugin) { + return plugin.GetVersionString().c_str(); } -#define GOLM_UTF8(str) ConvertWideToUtf8(str) -#define GOLM_PSTR(str) ConvertUtf8ToWide(str) -#else -#define GOLM_UTF8(str) str -#define GOLM_PSTR(str) str -#endif -const char* GetPluginBaseDir(PluginHandle plugin) { - auto source = GOLM_UTF8(plugin.GetBaseDir()); - size_t size = source.length() + 1; - char* dest = new char[size]; - std::memcpy(dest, source.data(), size); - return dest; +const char* GetPluginAuthor(const Extension& plugin) { + return plugin.GetAuthor().c_str(); } -const char* GetPluginConfigsDir(PluginHandle plugin) { - auto source = GOLM_UTF8(plugin.GetConfigsDir()); - size_t size = source.length() + 1; - char* dest = new char[size]; - std::memcpy(dest, source.data(), size); - return dest; +const char* GetPluginWebsite(const Extension& plugin) { + return plugin.GetWebsite().c_str(); } -const char* GetPluginDataDir(PluginHandle plugin) { - auto source = GOLM_UTF8(plugin.GetDataDir()); - size_t size = source.length() + 1; - char* dest = new char[size]; - std::memcpy(dest, source.data(), size); - return dest; +const char* GetPluginLicense(const Extension& plugin) { + return plugin.GetLicense().c_str(); } -const char* GetPluginLogsDir(PluginHandle plugin) { - auto source = GOLM_UTF8(plugin.GetLogsDir()); +const char* GetPluginLocation(const Extension& plugin) { + const auto& source = plg::as_string(plugin.GetLocation()); size_t size = source.length() + 1; char* dest = new char[size]; std::memcpy(dest, source.data(), size); return dest; } -const char** GetPluginDependencies(PluginHandle plugin) { - std::span dependencies = plugin.GetDescriptor().GetDependencies(); +const char** GetPluginDependencies(const Extension& plugin) { + const std::vector& dependencies = plugin.GetDependencies(); auto* deps = new const char*[dependencies.size()]; for (size_t i = 0; i < dependencies.size(); ++i) { - deps[i] = dependencies[i].GetName().data(); + deps[i] = dependencies[i].GetName().c_str(); } return deps; } -ptrdiff_t GetPluginDependenciesSize(PluginHandle plugin) { - return static_cast(plugin.GetDescriptor().GetDependencies().size()); -} - -const char* FindPluginResource(PluginHandle plugin, GoString path) { - auto resource = plugin.FindResource(GOLM_PSTR(path)); - if (resource.has_value()) { - auto source= GOLM_UTF8(*resource); - size_t size = source.length() + 1; - char* dest = new char[size]; - std::memcpy(dest, source.data(), size); - return dest; - } - return ""; +ptrdiff_t GetPluginDependenciesSize(const Extension& plugin) { + return static_cast(plugin.GetDependencies().size()); } void DeleteCStr(const char* str) { @@ -399,6 +356,8 @@ void DestroyVariant(plg::any* any) { any->~variant(); } +#undef as_string + namespace { template PLUGIFY_FORCE_INLINE plg::vector ConstructVector(T* arr, ptrdiff_t len) requires(!std::is_same_v) { @@ -575,7 +534,7 @@ void AssignVectorVector4(plg::vector* ptr, plg::vec4* arr, ptrdiff_t void AssignVectorMatrix4x4(plg::vector* ptr, plg::mat4x4* arr, ptrdiff_t len) { AssignVector(ptr, arr, len); } struct ManagedType { - plugify::ValueType type{}; + ValueType type{}; bool ref{}; }; @@ -592,20 +551,20 @@ JitCall* NewCall(void* target, ManagedType* params, ptrdiff_t count, ManagedType #endif bool retHidden = ValueUtils::IsHiddenParam(ret.type); - asmjit::FuncSignature sig(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs, JitUtils::GetRetTypeId(retHidden ? typeHidden : ret.type)); + Signature sig(CallConv::CDecl, retHidden ? typeHidden : ret.type); #if !GOLM_ARCH_ARM if (retHidden) { - sig.addArg(JitUtils::GetValueTypeId(ret.type)); + sig.AddArg(ret.type); } #endif for (ptrdiff_t i = 0; i < count; ++i) { const auto& [type, ref] = params[i]; - sig.addArg(JitUtils::GetValueTypeId(ref ? ValueType::Pointer : type)); + sig.AddArg(ref ? ValueType::Pointer : type); } - JitCall* call = new JitCall(g_golm.GetRuntime()); + JitCall* call = new JitCall{}; call->GetJitFunc(sig, target, JitCall::WaitType::None, retHidden); return call; } @@ -622,13 +581,13 @@ const char* GetCallError(JitCall* call) { return call ? call->GetError().data() : "Target invalid"; } -JitCallback* NewCallback(PluginHandle plugin, GoString name, void* delegate) { - MethodHandle method = g_golm.FindMethod(name); +JitCallback* NewCallback(const Extension& plugin, GoString name, void* delegate) { + const Method* method = g_golm.FindMethod(name); if (method == nullptr || delegate == nullptr) return nullptr; - JitCallback* callback = new JitCallback(g_golm.GetRuntime()); - callback->GetJitFunc(method, plugin.GetData().RCast()->callFunc, delegate); + JitCallback* callback = new JitCallback{}; + callback->GetJitFunc(*method, plugin.GetUserData().RCast()->callFunc, delegate); return callback; } @@ -644,57 +603,53 @@ const char* GetCallbackError(JitCallback* callback) { return callback ? callback->GetError().data() : "Method invalid"; } -ptrdiff_t GetMethodParamCount(MethodHandle handle) { +ptrdiff_t GetMethodParamCount(const Method& handle) { return static_cast(handle.GetParamTypes().size()); } -ManagedType GetMethodParamType(MethodHandle handle, ptrdiff_t index) { - PropertyHandle param; - if (index < 0) { - param = handle.GetReturnType(); - } else { - param = handle.GetParamTypes()[static_cast(index)]; - } - return { param.GetType(), param.IsReference() }; +ManagedType GetMethodParamType(const Method& handle, ptrdiff_t index) { + const Property& param = index < 0 ? handle.GetRetType() : handle.GetParamTypes()[static_cast(index)]; + return { param.GetType(), param.IsRef() }; } -MethodHandle GetMethodPrototype(MethodHandle handle, ptrdiff_t index) { +const Method& GetMethodPrototype(const Method& handle, ptrdiff_t index) { if (index < 0) { - return handle.GetReturnType().GetPrototype(); + return *handle.GetRetType().GetPrototype(); } else { - return handle.GetParamTypes()[static_cast(index)].GetPrototype(); + return *handle.GetParamTypes()[static_cast(index)].GetPrototype(); } } -EnumHandle GetMethodEnum(MethodHandle handle, ptrdiff_t index) { +const EnumObject& GetMethodEnum(const Method& handle, ptrdiff_t index) { if (index < 0) { - return handle.GetReturnType().GetEnum(); + return *handle.GetRetType().GetEnumerate(); } else { - return handle.GetParamTypes()[static_cast(index)].GetEnum(); + return *handle.GetParamTypes()[static_cast(index)].GetEnumerate(); } } -const std::array GoLanguageModule::_pluginApi = { +const std::array GoLanguageModule::_pluginApi = { reinterpret_cast(&::GetMethodPtr), reinterpret_cast(&::GetMethodPtr2), - reinterpret_cast(&::IsModuleLoaded), - reinterpret_cast(&::IsPluginLoaded), + reinterpret_cast(&::IsExtensionLoaded), + reinterpret_cast(&::GetBaseDir), + reinterpret_cast(&::GetExtensionsDir), + reinterpret_cast(&::GetConfigsDir), + reinterpret_cast(&::GetDataDir), + reinterpret_cast(&::GetLogsDir), + reinterpret_cast(&::GetCacheDir), reinterpret_cast(&::PrintException), reinterpret_cast(&::GetPluginId), reinterpret_cast(&::GetPluginName), - reinterpret_cast(&::GetPluginFullName), reinterpret_cast(&::GetPluginDescription), reinterpret_cast(&::GetPluginVersion), reinterpret_cast(&::GetPluginAuthor), reinterpret_cast(&::GetPluginWebsite), - reinterpret_cast(&::GetPluginBaseDir), - reinterpret_cast(&::GetPluginConfigsDir), - reinterpret_cast(&::GetPluginDataDir), - reinterpret_cast(&::GetPluginLogsDir), + reinterpret_cast(&::GetPluginLicense), + reinterpret_cast(&::GetPluginLocation), reinterpret_cast(&::GetPluginDependencies), reinterpret_cast(&::GetPluginDependenciesSize), - reinterpret_cast(&::FindPluginResource), reinterpret_cast(&::DeleteCStr), reinterpret_cast(&::DeleteCStrArr), diff --git a/src/module.hpp b/src/module.hpp index d9c138f..dcd0ce2 100644 --- a/src/module.hpp +++ b/src/module.hpp @@ -1,10 +1,11 @@ #pragma once -#include #include -#include +#include #include -#include +#include + +#include using GoInt8 = signed char; using GoUint8 = unsigned char; @@ -30,6 +31,7 @@ struct GoString { GoInt n; operator std::string_view() const { return {p, static_cast(n)}; } + operator bool() const { return n > 0; } }; using GoMap = void*; @@ -47,22 +49,12 @@ struct GoSlice { template operator std::span() const { return { static_cast(data), static_cast(len)}; } + operator bool() const { return len > 0; } }; -namespace golm { - struct string_hash { - using is_transparent = void; - [[nodiscard]] size_t operator()(const char* txt) const { - return std::hash{}(txt); - } - [[nodiscard]] size_t operator()(std::string_view txt) const { - return std::hash{}(txt); - } - [[nodiscard]] size_t operator()(const std::string& txt) const { - return std::hash{}(txt); - } - }; +using namespace plugify; +namespace golm { constexpr int kApiVersion = 1; struct PluginContext { @@ -73,56 +65,55 @@ namespace golm { }; using InitFunc = int (*)(GoSlice, int, const void*); + using CallFunc = JitCallback::CallbackHandler; using StartFunc = void (*)(); using UpdateFunc = void (*)(float); using EndFunc = void (*)(); using ContextFunc = PluginContext* (*)(); struct AssemblyHolder { - std::unique_ptr assembly; + std::shared_ptr assembly; UpdateFunc updateFunc; StartFunc startFunc; EndFunc endFunc; ContextFunc contextFunc; - plugify::JitCallback::CallbackHandler callFunc; + CallFunc callFunc; }; - class GoLanguageModule final : public plugify::ILanguageModule { + class GoLanguageModule final : public ILanguageModule { public: GoLanguageModule() = default; ~GoLanguageModule() = default; // ILanguageModule - plugify::InitResult Initialize(std::weak_ptr provider, plugify::ModuleHandle module) override; + Result Initialize(const Provider& provider, const Extension& module) override; void Shutdown() override; - void OnUpdate(plugify::DateTime dt) override; - plugify::LoadResult OnPluginLoad(plugify::PluginHandle plugin) override; - void OnPluginStart(plugify::PluginHandle plugin) override; - void OnPluginUpdate(plugify::PluginHandle plugin, plugify::DateTime dt) override; - void OnPluginEnd(plugify::PluginHandle plugin) override; - void OnMethodExport(plugify::PluginHandle plugin) override; + void OnUpdate(std::chrono::milliseconds dt) override; + Result OnPluginLoad(const Extension& plugin) override; + void OnPluginStart(const Extension& plugin) override; + void OnPluginUpdate(const Extension& plugin, std::chrono::milliseconds dt) override; + void OnPluginEnd(const Extension& plugin) override; + void OnMethodExport(const Extension& plugin) override; bool IsDebugBuild() override; - const std::shared_ptr& GetProvider() { return _provider; } - const std::shared_ptr& GetRuntime() { return _rt; } + const std::unique_ptr& GetProvider() { return _provider; } - plugify::MemAddr GetNativeMethod(std::string_view methodName) const; - void GetNativeMethod(std::string_view methodName, plugify::MemAddr* addressDest); - plugify::MethodHandle FindMethod(std::string_view name); + MemAddr GetNativeMethod(std::string_view methodName) const; + void GetNativeMethod(std::string_view methodName, MemAddr* addressDest); + const Method* FindMethod(std::string_view name); private: - std::shared_ptr _provider; - std::shared_ptr _rt; + std::unique_ptr _provider; std::vector> _assemblies; - std::unordered_map> _nativesMap; + std::unordered_map> _nativesMap; - std::vector _addresses; + std::vector _addresses; - static const std::array _pluginApi; + static const std::array _pluginApi; }; extern GoLanguageModule g_golm; } -extern "C" GOLM_EXPORT plugify::ILanguageModule* GetLanguageModule(); +extern "C" GOLM_EXPORT ILanguageModule* GetLanguageModule(); diff --git a/src/pch.hpp b/src/pch.hpp index 7125a1f..647ca22 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -17,5 +17,3 @@ #include namespace fs = std::filesystem; - -#include diff --git a/test/cross_call_worker/cross_call_worker.pplugin b/test/cross_call_worker/cross_call_worker.pplugin index 1ab25f6..fe19f19 100644 --- a/test/cross_call_worker/cross_call_worker.pplugin +++ b/test/cross_call_worker/cross_call_worker.pplugin @@ -1,21 +1,16 @@ { "$schema": "https://raw.githubusercontent.com/untrustedmodders/plugify/refs/heads/main/schemas/plugin.schema.json", - "fileVersion": 1, "version": "0.1.0", - "friendlyName": "Cross-call Worker", + "name": "cross_call_worker", "description": "Calls test plugin. Language specific implementation", - "createdBy": "untrustedmodders", - "createdByURL": "https://github.com/untrustedmodders/", - "docsURL": "", - "downloadURL": "", - "updateURL": "", - "entryPoint": "cross_call_worker", - "supportedPlatforms": [], - "languageModule": { - "name": "golang" - }, + "author": "untrustedmodders", + "website": "https://github.com/untrustedmodders/", + "license": "MIT", + "entry": "cross_call_worker", + "platforms": [], + "language": "golang", "dependencies": [], - "exportedMethods": [ + "methods": [ { "name": "NoParamReturnVoid", "funcName": "__NoParamReturnVoid", @@ -1745,7 +1740,15 @@ "paramTypes": [ ], "retType": { - "type": "function" + "type": "function", + "prototype": { + "name": "FuncFunctionInner", + "paramTypes": [ + ], + "retType": { + "type": "void" + } + } } } } diff --git a/test/cross_call_worker/go.mod b/test/cross_call_worker/go.mod index 440afd5..13fc14c 100644 --- a/test/cross_call_worker/go.mod +++ b/test/cross_call_worker/go.mod @@ -2,4 +2,4 @@ module plugify-plugin go 1.24.0 -require github.com/untrustedmodders/go-plugify v1.1.7 +require github.com/untrustedmodders/go-plugify v2.0.0 diff --git a/test/cross_call_worker/go.sum b/test/cross_call_worker/go.sum index 4e0211f..cb26f9b 100644 --- a/test/cross_call_worker/go.sum +++ b/test/cross_call_worker/go.sum @@ -1,17 +1 @@ -github.com/untrustedmodders/go-plugify v0.0.0-20250302220657-ee0cab0c0300 h1:OuqOGtXXkka7HwdGyHboZDcao64kMSOApo+/UfJM/EQ= -github.com/untrustedmodders/go-plugify v0.0.0-20250302220657-ee0cab0c0300/go.mod h1:uPlNwuLP2QQmFGr5JfoU43Kwu84kI6e7CT7pNx+0ulE= -github.com/untrustedmodders/go-plugify v1.0.1 h1:UsGfEjCkvnig4lLBooiQX212MLHwgla3gE2TiU573zE= -github.com/untrustedmodders/go-plugify v1.0.1/go.mod h1:oXbAbZFAOtp53D3d9z5MyZHQ9+cfeuENFiacO/l6AWk= -github.com/untrustedmodders/go-plugify v1.0.3 h1:9Z6U1DmArMMuJv7fiCygLyYkrrKj+fXMvaHY7aAVQdY= -github.com/untrustedmodders/go-plugify v1.0.3/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= -github.com/untrustedmodders/go-plugify v1.0.4 h1:qKf7QxjFSlnurnDVev7rzLgNgA5QrahDSlZMXVBrm0U= -github.com/untrustedmodders/go-plugify v1.0.4/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= -github.com/untrustedmodders/go-plugify v1.0.5 h1:UCeww6Z2kTmouCRsqqkM3PYDcRL4HzqVWNmzXsz738I= -github.com/untrustedmodders/go-plugify v1.0.5/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= -github.com/untrustedmodders/go-plugify v1.1.3/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= -github.com/untrustedmodders/go-plugify v1.1.4 h1:x85BExKXTqxYmDN39qalsIovEEJXSxSesHvzU4ye2Hc= -github.com/untrustedmodders/go-plugify v1.1.4/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= -github.com/untrustedmodders/go-plugify v1.1.6 h1:1vKBkMFQy+c4AkN1rsTU0dblLyVR75Bq/qfnvUQTy90= -github.com/untrustedmodders/go-plugify v1.1.6/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= -github.com/untrustedmodders/go-plugify v1.1.7 h1:dZM1e6zLxe+cvme2tSTbynGmehHtYdNt9jqlYCcD0Xc= -github.com/untrustedmodders/go-plugify v1.1.7/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= +github.com/untrustedmodders/go-plugify v1.1.11/go.mod h1:c5w+u1HBAOxLPJzBwT0OBpfokLuZxGvnjtNSTOmynWc= From 8aef780253267f158b88bcbd7a46f211770f787c Mon Sep 17 00:00:00 2001 From: qubka Date: Sat, 13 Sep 2025 02:25:33 +0100 Subject: [PATCH 5/7] fix: change function order --- src/module.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module.cpp b/src/module.cpp index 46734fb..f5ecef5 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -631,13 +631,13 @@ const EnumObject& GetMethodEnum(const Method& handle, ptrdiff_t index) { const std::array GoLanguageModule::_pluginApi = { reinterpret_cast(&::GetMethodPtr), reinterpret_cast(&::GetMethodPtr2), - reinterpret_cast(&::IsExtensionLoaded), reinterpret_cast(&::GetBaseDir), reinterpret_cast(&::GetExtensionsDir), reinterpret_cast(&::GetConfigsDir), reinterpret_cast(&::GetDataDir), reinterpret_cast(&::GetLogsDir), reinterpret_cast(&::GetCacheDir), + reinterpret_cast(&::IsExtensionLoaded), reinterpret_cast(&::PrintException), reinterpret_cast(&::GetPluginId), From 28d3e5b97560ef4fb78465f0b6454a8a1783a966 Mon Sep 17 00:00:00 2001 From: qubka Date: Sun, 14 Sep 2025 22:27:39 +0100 Subject: [PATCH 6/7] fix: update clang format --- .clang-format | 148 +++++++++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/.clang-format b/.clang-format index ac2c721..36e77ba 100644 --- a/.clang-format +++ b/.clang-format @@ -1,66 +1,92 @@ -# Generated from CLion C/C++ Code Style settings -BasedOnStyle: LLVM -AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: None -AlignOperands: Align +BasedOnStyle: Mozilla +Language: Cpp +Standard: c++20 + +AccessModifierOffset: "-4" +AlignAfterOpenBracket: BlockIndent +AlignEscapedNewlinesLeft: "false" AllowAllArgumentsOnNextLine: false -AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Always -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: Always -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterReturnType: None -AlwaysBreakTemplateDeclarations: Yes -BreakBeforeBraces: Custom -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: true -BreakBeforeBinaryOperators: None -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeColon -BreakInheritanceList: BeforeColon -ColumnLimit: 0 -CompactNamespaces: false -ContinuationIndentWidth: 8 -IndentCaseLabels: true -IndentPPDirectives: None -IndentWidth: 4 -KeepEmptyLinesAtTheStartOfBlocks: true -MaxEmptyLinesToKeep: 2 +AllowShortBlocksOnASingleLine: "false" +AllowShortCaseLabelsOnASingleLine: "false" +AllowShortFunctionsOnASingleLine: "false" +AllowShortIfStatementsOnASingleLine: "false" +AllowShortLoopsOnASingleLine: "false" +AlwaysBreakTemplateDeclarations: "true" +BreakAfterReturnType: ExceptShortType +BinPackArguments: false +BinPackParameters: false +BreakAfterAttributes: Leave +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: "true" +BreakConstructorInitializersBeforeComma: "true" +BreakStringLiterals: "false" +ColumnLimit: "100" +ConstructorInitializerAllOnOneLineOrOnePerLine: "false" +ConstructorInitializerIndentWidth: "4" +ContinuationIndentWidth: "4" +Cpp11BracedListStyle: "false" +DerivePointerAlignment: "false" +DisableFormat: "false" +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always +ExperimentalAutoDetectBinPacking: "true" +IncludeBlocks: Regroup +IncludeCategories: + - Regex: <[^.]+> + Priority: -3 + - Regex: + Priority: -1 + - Regex: <.+> + Priority: -2 + - Regex: '"plugify/.+"' + Priority: 0 + - Regex: '"glaze/.+"' + Priority: 0 + - Regex: '"asmjit/.+"' + Priority: 0 + - Regex: '".+/.+"' + Priority: 1 + - Regex: '".+"' + Priority: 2 +IndentCaseLabels: "true" +IndentWidth: "4" +IndentWrappedFunctionNames: "false" +InsertBraces: true # Experimental +KeepEmptyLinesAtTheStartOfBlocks: "false" +MaxEmptyLinesToKeep: "2" NamespaceIndentation: All -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true +ObjCBlockIndentWidth: "4" +ObjCSpaceAfterProperty: "false" +ObjCSpaceBeforeProtocolList: "false" +PackConstructorInitializers: Never +PenaltyBreakAssignment: 100000 +PenaltyBreakBeforeFirstCallParameter: 0 +PenaltyBreakComment: 10 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakTemplateDeclaration: 0 +PenaltyExcessCharacter: 10 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 10 PointerAlignment: Left -ReflowComments: false -SpaceAfterCStyleCast: true -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true +ReferenceAlignment: Left +QualifierAlignment: Custom # Experimental +QualifierOrder: [inline, static, constexpr, const, volatile, type] +ReflowComments: "true" +SeparateDefinitionBlocks: Always +SortIncludes: CaseInsensitive +SortUsingDeclarations: Never +SpaceAfterCStyleCast: "true" +SpaceAfterTemplateKeyword: "true" +SpaceBeforeAssignmentOperators: "true" SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 0 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -TabWidth: 4 -UseTab: Always \ No newline at end of file +SpaceInEmptyParentheses: "false" +SpacesBeforeTrailingComments: "2" +SpacesInAngles: "false" +SpacesInCStyleCastParentheses: "false" +SpacesInContainerLiterals: "false" +SpacesInParentheses: "false" +SpacesInSquareBrackets: "false" +TabWidth: "4" +UseTab: "ForIndentation" \ No newline at end of file From a289bd8dde808b78c7f8d1cb37053956891130f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:27:55 +0000 Subject: [PATCH 7/7] chore(main): release 2.0.0 --- .github/release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ version.txt | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/release-please-manifest.json b/.github/release-please-manifest.json index ab736c5..895bf0e 100644 --- a/.github/release-please-manifest.json +++ b/.github/release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.1.8" + ".": "2.0.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 89981f7..a3b64a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [2.0.0](https://github.com/untrustedmodders/plugify-module-golang/compare/v1.1.8...v2.0.0) (2025-09-14) + + +### ⚠ BREAKING CHANGES + +* update for a new plugify + +### Features + +* update for a new plugify ([ff091f8](https://github.com/untrustedmodders/plugify-module-golang/commit/ff091f801c501407145c6a8d2b1a7c0fee836856)) + + +### Bug Fixes + +* add prefix to all enum parameter names ([2c0347f](https://github.com/untrustedmodders/plugify-module-golang/commit/2c0347f688de410e626a9133cf43ec865a50c3c9)) +* change function order ([8aef780](https://github.com/untrustedmodders/plugify-module-golang/commit/8aef780253267f158b88bcbd7a46f211770f787c)) +* changing suffix to prefix ([7e206b2](https://github.com/untrustedmodders/plugify-module-golang/commit/7e206b2153e5bf0e043baac390a2d90271561692)) +* resolve enum constant name collisions and add side-effect reflect import ([0e39f68](https://github.com/untrustedmodders/plugify-module-golang/commit/0e39f684b65d3b573a48d745202faa2c208190eb)) +* update clang format ([28d3e5b](https://github.com/untrustedmodders/plugify-module-golang/commit/28d3e5b97560ef4fb78465f0b6454a8a1783a966)) + ## [1.1.8](https://github.com/untrustedmodders/plugify-module-golang/compare/v1.1.7...v1.1.8) (2025-08-02) diff --git a/version.txt b/version.txt index 18efdb9..227cea2 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.1.8 +2.0.0