diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..a8907c0 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +7.0.2 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4c0fa9f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "pip" + directory: "/src" + schedule: + interval: "weekly" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e880a90..742c31d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: bazel test //... env: # Bazelisk will download bazel to here diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ab36cc1..a22ac4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,10 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks +ci: + skip: + - buildifier + - buildifier-lint + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 @@ -15,15 +20,16 @@ repos: hooks: - id: black - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.13.2 hooks: - id: isort name: isort (python) - - repo: https://github.com/FelixSeptem/pre-commit-golang.git - rev: "12f5442f51377b10b26651ad745206bbe1500ad6" + - repo: https://github.com/keith/pre-commit-buildifier + rev: 6.4.0 hooks: - - id: bazel-buildifier - # - repo: https://github.com/pre-commit/mirrors-mypy - # rev: v0.991 - # hooks: - # - id: mypy + - id: buildifier + - id: buildifier-lint + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.991 + hooks: + - id: mypy diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..83fd0ca --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,15 @@ +bazel_dep(name = "rules_python", version = "0.31.0") + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + python_version = "3.10", +) + +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.parse( + python_version = "3.10", + requirements_lock = "//src:requirements.txt", +) +use_repo(pip, "py_proto_deps") + +bazel_dep(name = "bazel_skylib", version = "1.5.0") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 0000000..1291224 --- /dev/null +++ b/MODULE.bazel.lock @@ -0,0 +1,2312 @@ +{ + "lockFileVersion": 3, + "moduleFileHash": "a7f648186b9f5a8cdbf756bc0322a485caa56f6e95f1c6aa5fd7800be1042470", + "flags": { + "cmdRegistries": [ + "https://bcr.bazel.build/" + ], + "cmdModuleOverrides": {}, + "allowedYankedVersions": [], + "envVarAllowedYankedVersions": "", + "ignoreDevDependency": false, + "directDependenciesMode": "WARNING", + "compatibilityMode": "ERROR" + }, + "localOverrideHashes": { + "bazel_tools": "922ea6752dc9105de5af957f7a99a6933c0a6a712d23df6aad16a9c399f7e787" + }, + "moduleDepGraph": { + "": { + "name": "", + "version": "", + "key": "", + "repoName": "", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_python//python/extensions:python.bzl", + "extensionName": "python", + "usingModule": "", + "location": { + "file": "@@//:MODULE.bazel", + "line": 3, + "column": 23 + }, + "imports": {}, + "devImports": [], + "tags": [ + { + "tagName": "toolchain", + "attributeValues": { + "python_version": "3.10" + }, + "devDependency": false, + "location": { + "file": "@@//:MODULE.bazel", + "line": 4, + "column": 17 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@rules_python//python/extensions:pip.bzl", + "extensionName": "pip", + "usingModule": "", + "location": { + "file": "@@//:MODULE.bazel", + "line": 8, + "column": 20 + }, + "imports": { + "py_proto_deps": "py_proto_deps" + }, + "devImports": [], + "tags": [ + { + "tagName": "parse", + "attributeValues": { + "python_version": "3.10", + "requirements_lock": "//src:requirements.txt" + }, + "devDependency": false, + "location": { + "file": "@@//:MODULE.bazel", + "line": 9, + "column": 10 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "rules_python": "rules_python@0.31.0", + "bazel_skylib": "bazel_skylib@1.5.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + } + }, + "rules_python@0.31.0": { + "name": "rules_python", + "version": "0.31.0", + "key": "rules_python@0.31.0", + "repoName": "rules_python", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@pythons_hub//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_python//python/private/bzlmod:internal_deps.bzl", + "extensionName": "internal_deps", + "usingModule": "rules_python@0.31.0", + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel", + "line": 15, + "column": 30 + }, + "imports": { + "rules_python_internal": "rules_python_internal", + "pypi__build": "pypi__build", + "pypi__click": "pypi__click", + "pypi__colorama": "pypi__colorama", + "pypi__importlib_metadata": "pypi__importlib_metadata", + "pypi__installer": "pypi__installer", + "pypi__more_itertools": "pypi__more_itertools", + "pypi__packaging": "pypi__packaging", + "pypi__pep517": "pypi__pep517", + "pypi__pip": "pypi__pip", + "pypi__pip_tools": "pypi__pip_tools", + "pypi__pyproject_hooks": "pypi__pyproject_hooks", + "pypi__setuptools": "pypi__setuptools", + "pypi__tomli": "pypi__tomli", + "pypi__wheel": "pypi__wheel", + "pypi__zipp": "pypi__zipp" + }, + "devImports": [], + "tags": [ + { + "tagName": "install", + "attributeValues": {}, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel", + "line": 16, + "column": 22 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@rules_python//python/extensions:python.bzl", + "extensionName": "python", + "usingModule": "rules_python@0.31.0", + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel", + "line": 41, + "column": 23 + }, + "imports": { + "pythons_hub": "pythons_hub" + }, + "devImports": [], + "tags": [ + { + "tagName": "toolchain", + "attributeValues": { + "is_default": true, + "python_version": "3.11" + }, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel", + "line": 47, + "column": 17 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_features": "bazel_features@1.1.1", + "bazel_skylib": "bazel_skylib@1.5.0", + "platforms": "platforms@0.0.7", + "rules_proto": "rules_proto@5.3.0-21.7", + "com_google_protobuf": "protobuf@21.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0", + "urls": [ + "https://github.com/bazelbuild/rules_python/releases/download/0.31.0/rules_python-0.31.0.tar.gz" + ], + "integrity": "sha256-xovcT77CXeW1STuIGc/Id8TqKZwNyxXCRMWgAgjN4xE=", + "strip_prefix": "rules_python-0.31.0", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_python/0.31.0/patches/module_dot_bazel_version.patch": "sha256-j2KF6j66J2fRAGtc56Zj7Hp1dTGqOWPAR3+IODr0oLQ=" + }, + "remote_patch_strip": 1 + } + } + }, + "bazel_skylib@1.5.0": { + "name": "bazel_skylib", + "version": "1.5.0", + "key": "bazel_skylib@1.5.0", + "repoName": "bazel_skylib", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "//toolchains/unittest:cmd_toolchain", + "//toolchains/unittest:bash_toolchain" + ], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "bazel_skylib~1.5.0", + "urls": [ + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz" + ], + "integrity": "sha256-zVWgYudjuTSZIfD124w5MyiNyLpPdt2UFqrGis7jy5Q=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "bazel_tools@_": { + "name": "bazel_tools", + "version": "", + "key": "bazel_tools@_", + "repoName": "bazel_tools", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@local_config_cc_toolchains//:all", + "@local_config_sh//:local_sh_toolchain" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl", + "extensionName": "cc_configure_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 17, + "column": 29 + }, + "imports": { + "local_config_cc": "local_config_cc", + "local_config_cc_toolchains": "local_config_cc_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/osx:xcode_configure.bzl", + "extensionName": "xcode_configure_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 21, + "column": 32 + }, + "imports": { + "local_config_xcode": "local_config_xcode" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@rules_java//java:extensions.bzl", + "extensionName": "toolchains", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 24, + "column": 32 + }, + "imports": { + "local_jdk": "local_jdk", + "remote_java_tools": "remote_java_tools", + "remote_java_tools_linux": "remote_java_tools_linux", + "remote_java_tools_windows": "remote_java_tools_windows", + "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64", + "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/sh:sh_configure.bzl", + "extensionName": "sh_configure_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 35, + "column": 39 + }, + "imports": { + "local_config_sh": "local_config_sh" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/test:extensions.bzl", + "extensionName": "remote_coverage_tools_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 39, + "column": 48 + }, + "imports": { + "remote_coverage_tools": "remote_coverage_tools" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/android:android_extensions.bzl", + "extensionName": "remote_android_tools_extensions", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 42, + "column": 42 + }, + "imports": { + "android_gmaven_r8": "android_gmaven_r8", + "android_tools": "android_tools" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "rules_cc": "rules_cc@0.0.9", + "rules_java": "rules_java@7.1.0", + "rules_license": "rules_license@0.0.7", + "rules_proto": "rules_proto@5.3.0-21.7", + "rules_python": "rules_python@0.31.0", + "platforms": "platforms@0.0.7", + "com_google_protobuf": "protobuf@21.7", + "zlib": "zlib@1.3", + "build_bazel_apple_support": "apple_support@1.5.0", + "local_config_platform": "local_config_platform@_" + } + }, + "local_config_platform@_": { + "name": "local_config_platform", + "version": "", + "key": "local_config_platform@_", + "repoName": "local_config_platform", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_" + } + }, + "bazel_features@1.1.1": { + "name": "bazel_features", + "version": "1.1.1", + "key": "bazel_features@1.1.1", + "repoName": "bazel_features", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@bazel_features//private:extensions.bzl", + "extensionName": "version_extension", + "usingModule": "bazel_features@1.1.1", + "location": { + "file": "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel", + "line": 6, + "column": 24 + }, + "imports": { + "bazel_features_globals": "bazel_features_globals", + "bazel_features_version": "bazel_features_version" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "bazel_features~1.1.1", + "urls": [ + "https://github.com/bazel-contrib/bazel_features/releases/download/v1.1.1/bazel_features-v1.1.1.tar.gz" + ], + "integrity": "sha256-YsJuQn5cvHUQJERpJ2IuOYqdzfMsZDJSOIFXCdEcEag=", + "strip_prefix": "bazel_features-1.1.1", + "remote_patches": { + "https://bcr.bazel.build/modules/bazel_features/1.1.1/patches/module_dot_bazel_version.patch": "sha256-+56MAEsc7bYN/Pzhn252ZQUxiRzZg9bynXj1qpsmCYs=" + }, + "remote_patch_strip": 1 + } + } + }, + "platforms@0.0.7": { + "name": "platforms", + "version": "0.0.7", + "key": "platforms@0.0.7", + "repoName": "platforms", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "rules_license": "rules_license@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "platforms", + "urls": [ + "https://github.com/bazelbuild/platforms/releases/download/0.0.7/platforms-0.0.7.tar.gz" + ], + "integrity": "sha256-OlYcmee9vpFzqmU/1Xn+hJ8djWc5V4CrR3Cx84FDHVE=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_proto@5.3.0-21.7": { + "name": "rules_proto", + "version": "5.3.0-21.7", + "key": "rules_proto@5.3.0-21.7", + "repoName": "rules_proto", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.5.0", + "com_google_protobuf": "protobuf@21.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_proto~5.3.0-21.7", + "urls": [ + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz" + ], + "integrity": "sha256-3D+yBqLLNEG0heseQjFlsjEjWh6psDG0Qzz3vB+kYN0=", + "strip_prefix": "rules_proto-5.3.0-21.7", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "protobuf@21.7": { + "name": "protobuf", + "version": "21.7", + "key": "protobuf@21.7", + "repoName": "protobuf", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_jvm_external//:extensions.bzl", + "extensionName": "maven", + "usingModule": "protobuf@21.7", + "location": { + "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel", + "line": 22, + "column": 22 + }, + "imports": { + "maven": "maven" + }, + "devImports": [], + "tags": [ + { + "tagName": "install", + "attributeValues": { + "name": "maven", + "artifacts": [ + "com.google.code.findbugs:jsr305:3.0.2", + "com.google.code.gson:gson:2.8.9", + "com.google.errorprone:error_prone_annotations:2.3.2", + "com.google.j2objc:j2objc-annotations:1.3", + "com.google.guava:guava:31.1-jre", + "com.google.guava:guava-testlib:31.1-jre", + "com.google.truth:truth:1.1.2", + "junit:junit:4.13.2", + "org.mockito:mockito-core:4.3.1" + ] + }, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel", + "line": 24, + "column": 14 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.5.0", + "rules_python": "rules_python@0.31.0", + "rules_cc": "rules_cc@0.0.9", + "rules_proto": "rules_proto@5.3.0-21.7", + "rules_java": "rules_java@7.1.0", + "rules_pkg": "rules_pkg@0.7.0", + "com_google_abseil": "abseil-cpp@20211102.0", + "zlib": "zlib@1.3", + "upb": "upb@0.0.0-20220923-a547704", + "rules_jvm_external": "rules_jvm_external@4.4.2", + "com_google_googletest": "googletest@1.11.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "protobuf~21.7", + "urls": [ + "https://github.com/protocolbuffers/protobuf/releases/download/v21.7/protobuf-all-21.7.zip" + ], + "integrity": "sha256-VJOiH17T/FAuZv7GuUScBqVRztYwAvpIkDxA36jeeko=", + "strip_prefix": "protobuf-21.7", + "remote_patches": { + "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel.patch": "sha256-q3V2+eq0v2XF0z8z+V+QF4cynD6JvHI1y3kI/+rzl5s=", + "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel_for_examples.patch": "sha256-O7YP6s3lo/1opUiO0jqXYORNHdZ/2q3hjz1QGy8QdIU=", + "https://bcr.bazel.build/modules/protobuf/21.7/patches/relative_repo_names.patch": "sha256-RK9RjW8T5UJNG7flIrnFiNE9vKwWB+8uWWtJqXYT0w4=", + "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_missing_files.patch": "sha256-Hyne4DG2u5bXcWHNxNMirA2QFAe/2Cl8oMm1XJdkQIY=" + }, + "remote_patch_strip": 1 + } + } + }, + "rules_cc@0.0.9": { + "name": "rules_cc", + "version": "0.0.9", + "key": "rules_cc@0.0.9", + "repoName": "rules_cc", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@local_config_cc_toolchains//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl", + "extensionName": "cc_configure_extension", + "usingModule": "rules_cc@0.0.9", + "location": { + "file": "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel", + "line": 9, + "column": 29 + }, + "imports": { + "local_config_cc_toolchains": "local_config_cc_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_cc~0.0.9", + "urls": [ + "https://github.com/bazelbuild/rules_cc/releases/download/0.0.9/rules_cc-0.0.9.tar.gz" + ], + "integrity": "sha256-IDeHW5pEVtzkp50RKorohbvEqtlo5lh9ym5k86CQDN8=", + "strip_prefix": "rules_cc-0.0.9", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_cc/0.0.9/patches/module_dot_bazel_version.patch": "sha256-mM+qzOI0SgAdaJBlWOSMwMPKpaA9b7R37Hj/tp5bb4g=" + }, + "remote_patch_strip": 0 + } + } + }, + "rules_java@7.1.0": { + "name": "rules_java", + "version": "7.1.0", + "key": "rules_java@7.1.0", + "repoName": "rules_java", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "//toolchains:all", + "@local_jdk//:runtime_toolchain_definition", + "@local_jdk//:bootstrap_runtime_toolchain_definition", + "@remotejdk11_linux_toolchain_config_repo//:all", + "@remotejdk11_linux_aarch64_toolchain_config_repo//:all", + "@remotejdk11_linux_ppc64le_toolchain_config_repo//:all", + "@remotejdk11_linux_s390x_toolchain_config_repo//:all", + "@remotejdk11_macos_toolchain_config_repo//:all", + "@remotejdk11_macos_aarch64_toolchain_config_repo//:all", + "@remotejdk11_win_toolchain_config_repo//:all", + "@remotejdk11_win_arm64_toolchain_config_repo//:all", + "@remotejdk17_linux_toolchain_config_repo//:all", + "@remotejdk17_linux_aarch64_toolchain_config_repo//:all", + "@remotejdk17_linux_ppc64le_toolchain_config_repo//:all", + "@remotejdk17_linux_s390x_toolchain_config_repo//:all", + "@remotejdk17_macos_toolchain_config_repo//:all", + "@remotejdk17_macos_aarch64_toolchain_config_repo//:all", + "@remotejdk17_win_toolchain_config_repo//:all", + "@remotejdk17_win_arm64_toolchain_config_repo//:all", + "@remotejdk21_linux_toolchain_config_repo//:all", + "@remotejdk21_linux_aarch64_toolchain_config_repo//:all", + "@remotejdk21_macos_toolchain_config_repo//:all", + "@remotejdk21_macos_aarch64_toolchain_config_repo//:all", + "@remotejdk21_win_toolchain_config_repo//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_java//java:extensions.bzl", + "extensionName": "toolchains", + "usingModule": "rules_java@7.1.0", + "location": { + "file": "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel", + "line": 19, + "column": 27 + }, + "imports": { + "remote_java_tools": "remote_java_tools", + "remote_java_tools_linux": "remote_java_tools_linux", + "remote_java_tools_windows": "remote_java_tools_windows", + "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64", + "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64", + "local_jdk": "local_jdk", + "remotejdk11_linux_toolchain_config_repo": "remotejdk11_linux_toolchain_config_repo", + "remotejdk11_linux_aarch64_toolchain_config_repo": "remotejdk11_linux_aarch64_toolchain_config_repo", + "remotejdk11_linux_ppc64le_toolchain_config_repo": "remotejdk11_linux_ppc64le_toolchain_config_repo", + "remotejdk11_linux_s390x_toolchain_config_repo": "remotejdk11_linux_s390x_toolchain_config_repo", + "remotejdk11_macos_toolchain_config_repo": "remotejdk11_macos_toolchain_config_repo", + "remotejdk11_macos_aarch64_toolchain_config_repo": "remotejdk11_macos_aarch64_toolchain_config_repo", + "remotejdk11_win_toolchain_config_repo": "remotejdk11_win_toolchain_config_repo", + "remotejdk11_win_arm64_toolchain_config_repo": "remotejdk11_win_arm64_toolchain_config_repo", + "remotejdk17_linux_toolchain_config_repo": "remotejdk17_linux_toolchain_config_repo", + "remotejdk17_linux_aarch64_toolchain_config_repo": "remotejdk17_linux_aarch64_toolchain_config_repo", + "remotejdk17_linux_ppc64le_toolchain_config_repo": "remotejdk17_linux_ppc64le_toolchain_config_repo", + "remotejdk17_linux_s390x_toolchain_config_repo": "remotejdk17_linux_s390x_toolchain_config_repo", + "remotejdk17_macos_toolchain_config_repo": "remotejdk17_macos_toolchain_config_repo", + "remotejdk17_macos_aarch64_toolchain_config_repo": "remotejdk17_macos_aarch64_toolchain_config_repo", + "remotejdk17_win_toolchain_config_repo": "remotejdk17_win_toolchain_config_repo", + "remotejdk17_win_arm64_toolchain_config_repo": "remotejdk17_win_arm64_toolchain_config_repo", + "remotejdk21_linux_toolchain_config_repo": "remotejdk21_linux_toolchain_config_repo", + "remotejdk21_linux_aarch64_toolchain_config_repo": "remotejdk21_linux_aarch64_toolchain_config_repo", + "remotejdk21_macos_toolchain_config_repo": "remotejdk21_macos_toolchain_config_repo", + "remotejdk21_macos_aarch64_toolchain_config_repo": "remotejdk21_macos_aarch64_toolchain_config_repo", + "remotejdk21_win_toolchain_config_repo": "remotejdk21_win_toolchain_config_repo" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "platforms": "platforms@0.0.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_skylib": "bazel_skylib@1.5.0", + "rules_proto": "rules_proto@5.3.0-21.7", + "rules_license": "rules_license@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0", + "urls": [ + "https://github.com/bazelbuild/rules_java/releases/download/7.1.0/rules_java-7.1.0.tar.gz" + ], + "integrity": "sha256-o3pOX2OrgnFuXdau75iO2EYcegC46TYnImKJn1h81OE=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_license@0.0.7": { + "name": "rules_license", + "version": "0.0.7", + "key": "rules_license@0.0.7", + "repoName": "rules_license", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_license~0.0.7", + "urls": [ + "https://github.com/bazelbuild/rules_license/releases/download/0.0.7/rules_license-0.0.7.tar.gz" + ], + "integrity": "sha256-RTHezLkTY5ww5cdRKgVNXYdWmNrrddjPkPKEN1/nw2A=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "zlib@1.3": { + "name": "zlib", + "version": "1.3", + "key": "zlib@1.3", + "repoName": "zlib", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "zlib~1.3", + "urls": [ + "https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz" + ], + "integrity": "sha256-/wukwpIBPbwnUws6geH5qBPNOd4Byl4Pi/NVcC76WT4=", + "strip_prefix": "zlib-1.3", + "remote_patches": { + "https://bcr.bazel.build/modules/zlib/1.3/patches/add_build_file.patch": "sha256-Ei+FYaaOo7A3jTKunMEodTI0Uw5NXQyZEcboMC8JskY=", + "https://bcr.bazel.build/modules/zlib/1.3/patches/module_dot_bazel.patch": "sha256-fPWLM+2xaF/kuy+kZc1YTfW6hNjrkG400Ho7gckuyJk=" + }, + "remote_patch_strip": 0 + } + } + }, + "apple_support@1.5.0": { + "name": "apple_support", + "version": "1.5.0", + "key": "apple_support@1.5.0", + "repoName": "build_bazel_apple_support", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@local_config_apple_cc_toolchains//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@build_bazel_apple_support//crosstool:setup.bzl", + "extensionName": "apple_cc_configure_extension", + "usingModule": "apple_support@1.5.0", + "location": { + "file": "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel", + "line": 17, + "column": 35 + }, + "imports": { + "local_config_apple_cc": "local_config_apple_cc", + "local_config_apple_cc_toolchains": "local_config_apple_cc_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.5.0", + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "apple_support~1.5.0", + "urls": [ + "https://github.com/bazelbuild/apple_support/releases/download/1.5.0/apple_support.1.5.0.tar.gz" + ], + "integrity": "sha256-miM41vja0yRPgj8txghKA+TQ+7J8qJLclw5okNW0gYQ=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_pkg@0.7.0": { + "name": "rules_pkg", + "version": "0.7.0", + "key": "rules_pkg@0.7.0", + "repoName": "rules_pkg", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "rules_python": "rules_python@0.31.0", + "bazel_skylib": "bazel_skylib@1.5.0", + "rules_license": "rules_license@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_pkg~0.7.0", + "urls": [ + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz" + ], + "integrity": "sha256-iimOgydi7aGDBZfWT+fbWBeKqEzVkm121bdE1lWJQcI=", + "strip_prefix": "", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/patches/module_dot_bazel.patch": "sha256-4OaEPZwYF6iC71ZTDg6MJ7LLqX7ZA0/kK4mT+4xKqiE=" + }, + "remote_patch_strip": 0 + } + } + }, + "abseil-cpp@20211102.0": { + "name": "abseil-cpp", + "version": "20211102.0", + "key": "abseil-cpp@20211102.0", + "repoName": "abseil-cpp", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "rules_cc": "rules_cc@0.0.9", + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "abseil-cpp~20211102.0", + "urls": [ + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.tar.gz" + ], + "integrity": "sha256-3PcbnLqNwMqZQMSzFqDHlr6Pq0KwcLtrfKtitI8OZsQ=", + "strip_prefix": "abseil-cpp-20211102.0", + "remote_patches": { + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/patches/module_dot_bazel.patch": "sha256-4izqopgGCey4jVZzl/w3M2GVPNohjh2B5TmbThZNvPY=" + }, + "remote_patch_strip": 0 + } + } + }, + "upb@0.0.0-20220923-a547704": { + "name": "upb", + "version": "0.0.0-20220923-a547704", + "key": "upb@0.0.0-20220923-a547704", + "repoName": "upb", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.5.0", + "rules_proto": "rules_proto@5.3.0-21.7", + "com_google_protobuf": "protobuf@21.7", + "com_google_absl": "abseil-cpp@20211102.0", + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "upb~0.0.0-20220923-a547704", + "urls": [ + "https://github.com/protocolbuffers/upb/archive/a5477045acaa34586420942098f5fecd3570f577.tar.gz" + ], + "integrity": "sha256-z39x6v+QskwaKLSWRan/A6mmwecTQpHOcJActj5zZLU=", + "strip_prefix": "upb-a5477045acaa34586420942098f5fecd3570f577", + "remote_patches": { + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/patches/module_dot_bazel.patch": "sha256-wH4mNS6ZYy+8uC0HoAft/c7SDsq2Kxf+J8dUakXhaB0=" + }, + "remote_patch_strip": 0 + } + } + }, + "rules_jvm_external@4.4.2": { + "name": "rules_jvm_external", + "version": "4.4.2", + "key": "rules_jvm_external@4.4.2", + "repoName": "rules_jvm_external", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_jvm_external//:non-module-deps.bzl", + "extensionName": "non_module_deps", + "usingModule": "rules_jvm_external@4.4.2", + "location": { + "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel", + "line": 9, + "column": 32 + }, + "imports": { + "io_bazel_rules_kotlin": "io_bazel_rules_kotlin" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": ":extensions.bzl", + "extensionName": "maven", + "usingModule": "rules_jvm_external@4.4.2", + "location": { + "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel", + "line": 16, + "column": 22 + }, + "imports": { + "rules_jvm_external_deps": "rules_jvm_external_deps" + }, + "devImports": [], + "tags": [ + { + "tagName": "install", + "attributeValues": { + "name": "rules_jvm_external_deps", + "artifacts": [ + "com.google.cloud:google-cloud-core:1.93.10", + "com.google.cloud:google-cloud-storage:1.113.4", + "com.google.code.gson:gson:2.9.0", + "org.apache.maven:maven-artifact:3.8.6", + "software.amazon.awssdk:s3:2.17.183" + ], + "lock_file": "@rules_jvm_external//:rules_jvm_external_deps_install.json" + }, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel", + "line": 18, + "column": 14 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.5.0", + "io_bazel_stardoc": "stardoc@0.5.1", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_jvm_external~4.4.2", + "urls": [ + "https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.4.2.zip" + ], + "integrity": "sha256-c1YC9QgT6y6pPKP15DsZWb2AshO4NqB6YqKddXZwt3s=", + "strip_prefix": "rules_jvm_external-4.4.2", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "googletest@1.11.0": { + "name": "googletest", + "version": "1.11.0", + "key": "googletest@1.11.0", + "repoName": "googletest", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "com_google_absl": "abseil-cpp@20211102.0", + "platforms": "platforms@0.0.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "googletest~1.11.0", + "urls": [ + "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz" + ], + "integrity": "sha256-tIcL8SH/d5W6INILzdhie44Ijy0dqymaAxwQNO3ck9U=", + "strip_prefix": "googletest-release-1.11.0", + "remote_patches": { + "https://bcr.bazel.build/modules/googletest/1.11.0/patches/module_dot_bazel.patch": "sha256-HuahEdI/n8KCI071sN3CEziX+7qP/Ec77IWayYunLP0=" + }, + "remote_patch_strip": 0 + } + } + }, + "stardoc@0.5.1": { + "name": "stardoc", + "version": "0.5.1", + "key": "stardoc@0.5.1", + "repoName": "stardoc", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.5.0", + "rules_java": "rules_java@7.1.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "stardoc~0.5.1", + "urls": [ + "https://github.com/bazelbuild/stardoc/releases/download/0.5.1/stardoc-0.5.1.tar.gz" + ], + "integrity": "sha256-qoFNrgrEALurLoiB+ZFcb0fElmS/CHxAmhX5BDjSwj4=", + "strip_prefix": "", + "remote_patches": { + "https://bcr.bazel.build/modules/stardoc/0.5.1/patches/module_dot_bazel.patch": "sha256-UAULCuTpJE7SG0YrR9XLjMfxMRmbP+za3uW9ONZ5rjI=" + }, + "remote_patch_strip": 0 + } + } + } + }, + "moduleExtensions": { + "@@apple_support~1.5.0//crosstool:setup.bzl%apple_cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "pMLFCYaRPkgXPQ8vtuNkMfiHfPmRBy6QJfnid4sWfv0=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_apple_cc": { + "bzlFile": "@@apple_support~1.5.0//crosstool:setup.bzl", + "ruleClassName": "_apple_cc_autoconf", + "attributes": { + "name": "apple_support~1.5.0~apple_cc_configure_extension~local_config_apple_cc" + } + }, + "local_config_apple_cc_toolchains": { + "bzlFile": "@@apple_support~1.5.0//crosstool:setup.bzl", + "ruleClassName": "_apple_cc_autoconf_toolchains", + "attributes": { + "name": "apple_support~1.5.0~apple_cc_configure_extension~local_config_apple_cc_toolchains" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support~1.5.0", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@bazel_tools//tools/cpp:cc_configure.bzl%cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "mcsWHq3xORJexV5/4eCvNOLxFOQKV6eli3fkr+tEaqE=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_cc": { + "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl", + "ruleClassName": "cc_autoconf", + "attributes": { + "name": "bazel_tools~cc_configure_extension~local_config_cc" + } + }, + "local_config_cc_toolchains": { + "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl", + "ruleClassName": "cc_autoconf_toolchains", + "attributes": { + "name": "bazel_tools~cc_configure_extension~local_config_cc_toolchains" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "bazel_tools", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@bazel_tools//tools/osx:xcode_configure.bzl%xcode_configure_extension": { + "general": { + "bzlTransitiveDigest": "Qh2bWTU6QW6wkrd87qrU4YeY+SG37Nvw3A0PR4Y0L2Y=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_xcode": { + "bzlFile": "@@bazel_tools//tools/osx:xcode_configure.bzl", + "ruleClassName": "xcode_autoconf", + "attributes": { + "name": "bazel_tools~xcode_configure_extension~local_config_xcode", + "xcode_locator": "@bazel_tools//tools/osx:xcode_locator.m", + "remote_xcode": "" + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@bazel_tools//tools/sh:sh_configure.bzl%sh_configure_extension": { + "general": { + "bzlTransitiveDigest": "hp4NgmNjEg5+xgvzfh6L83bt9/aiiWETuNpwNuF1MSU=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_sh": { + "bzlFile": "@@bazel_tools//tools/sh:sh_configure.bzl", + "ruleClassName": "sh_config", + "attributes": { + "name": "bazel_tools~sh_configure_extension~local_config_sh" + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@rules_java~7.1.0//java:extensions.bzl%toolchains": { + "general": { + "bzlTransitiveDigest": "D02GmifxnV/IhYgspsJMDZ/aE8HxAjXgek5gi6FSto4=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "remotejdk21_linux_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_linux_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux//:jdk\",\n)\n" + } + }, + "remotejdk17_linux_s390x_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_s390x_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\n" + } + }, + "remotejdk17_macos_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_macos_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos//:jdk\",\n)\n" + } + }, + "remotejdk21_macos_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_macos_aarch64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk17_linux_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_aarch64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk21_macos_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_macos_aarch64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "2a7a99a3ea263dbd8d32a67d1e6e363ba8b25c645c826f5e167a02bbafaff1fa", + "strip_prefix": "zulu21.28.85-ca-jdk21.0.0-macosx_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-macosx_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-macosx_aarch64.tar.gz" + ] + } + }, + "remotejdk17_linux_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux//:jdk\",\n)\n" + } + }, + "remotejdk17_macos_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_macos_aarch64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "314b04568ec0ae9b36ba03c9cbd42adc9e1265f74678923b19297d66eb84dcca", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz" + ] + } + }, + "remote_java_tools_windows": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remote_java_tools_windows", + "sha256": "c5c70c214a350f12cbf52da8270fa43ba629b795f3dd328028a38f8f0d39c2a1", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.1/java_tools_windows-v13.1.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.1/java_tools_windows-v13.1.zip" + ] + } + }, + "remotejdk11_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_win", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "43408193ce2fa0862819495b5ae8541085b95660153f2adcf91a52d3a1710e83", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-win_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip" + ] + } + }, + "remotejdk11_win_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_win_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_aarch64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "54174439f2b3fddd11f1048c397fe7bb45d4c9d66d452d6889b013d04d21c4de", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz" + ] + } + }, + "remotejdk17_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "b9482f2304a1a68a614dfacddcf29569a72f0fac32e6c74f83dc1b9a157b8340", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz" + ] + } + }, + "remotejdk11_linux_s390x_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_s390x_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux//:jdk\",\n)\n" + } + }, + "remotejdk11_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_macos", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "bcaab11cfe586fae7583c6d9d311c64384354fb2638eb9a012eca4c3f1a1d9fd", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz" + ] + } + }, + "remotejdk11_win_arm64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_win_arm64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "b8a28e6e767d90acf793ea6f5bed0bb595ba0ba5ebdf8b99f395266161e53ec2", + "strip_prefix": "jdk-11.0.13+8", + "urls": [ + "https://mirror.bazel.build/aka.ms/download-jdk/microsoft-jdk-11.0.13.8.1-windows-aarch64.zip" + ] + } + }, + "remotejdk17_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_macos", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "640453e8afe8ffe0fb4dceb4535fb50db9c283c64665eebb0ba68b19e65f4b1f", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz" + ] + } + }, + "remotejdk21_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_macos", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "9639b87db586d0c89f7a9892ae47f421e442c64b97baebdff31788fbe23265bd", + "strip_prefix": "zulu21.28.85-ca-jdk21.0.0-macosx_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-macosx_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-macosx_x64.tar.gz" + ] + } + }, + "remotejdk21_macos_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_macos_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos//:jdk\",\n)\n" + } + }, + "remotejdk17_macos_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_macos_aarch64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk17_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_win", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "192f2afca57701de6ec496234f7e45d971bf623ff66b8ee4a5c81582054e5637", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip" + ] + } + }, + "remotejdk11_macos_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_macos_aarch64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_ppc64le_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_ppc64le_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\n" + } + }, + "remotejdk21_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_linux", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "0c0eadfbdc47a7ca64aeab51b9c061f71b6e4d25d2d87674512e9b6387e9e3a6", + "strip_prefix": "zulu21.28.85-ca-jdk21.0.0-linux_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-linux_x64.tar.gz" + ] + } + }, + "remote_java_tools_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remote_java_tools_linux", + "sha256": "d134da9b04c9023fb6e56a5d4bffccee73f7bc9572ddc4e747778dacccd7a5a7", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.1/java_tools_linux-v13.1.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.1/java_tools_linux-v13.1.zip" + ] + } + }, + "remotejdk21_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_win", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "e9959d500a0d9a7694ac243baf657761479da132f0f94720cbffd092150bd802", + "strip_prefix": "zulu21.28.85-ca-jdk21.0.0-win_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-win_x64.zip" + ] + } + }, + "remotejdk21_linux_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_linux_aarch64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "1fb64b8036c5d463d8ab59af06bf5b6b006811e6012e3b0eb6bccf57f1c55835", + "strip_prefix": "zulu21.28.85-ca-jdk21.0.0-linux_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-linux_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.28.85-ca-jdk21.0.0-linux_aarch64.tar.gz" + ] + } + }, + "remotejdk11_linux_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_aarch64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_s390x": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_s390x", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "a58fc0361966af0a5d5a31a2d8a208e3c9bb0f54f345596fd80b99ea9a39788b", + "strip_prefix": "jdk-11.0.15+10", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz", + "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz" + ] + } + }, + "remotejdk17_linux_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_aarch64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "6531cef61e416d5a7b691555c8cf2bdff689201b8a001ff45ab6740062b44313", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz" + ] + } + }, + "remotejdk17_win_arm64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_win_arm64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\n" + } + }, + "remotejdk11_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "a34b404f87a08a61148b38e1416d837189e1df7a040d949e743633daf4695a3c", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz" + ] + } + }, + "remotejdk11_macos_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_macos_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos//:jdk\",\n)\n" + } + }, + "remotejdk17_linux_ppc64le_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_ppc64le_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\n" + } + }, + "remotejdk17_win_arm64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_win_arm64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "6802c99eae0d788e21f52d03cab2e2b3bf42bc334ca03cbf19f71eb70ee19f85", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip" + ] + } + }, + "remote_java_tools_darwin_arm64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remote_java_tools_darwin_arm64", + "sha256": "dab5bb87ec43e980faea6e1cec14bafb217b8e2f5346f53aa784fd715929a930", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.1/java_tools_darwin_arm64-v13.1.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.1/java_tools_darwin_arm64-v13.1.zip" + ] + } + }, + "remotejdk17_linux_ppc64le": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_ppc64le", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "00a4c07603d0218cd678461b5b3b7e25b3253102da4022d31fc35907f21a2efd", + "strip_prefix": "jdk-17.0.8.1+1", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz", + "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz" + ] + } + }, + "remotejdk21_linux_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_linux_aarch64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk11_win_arm64_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_win_arm64_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\n" + } + }, + "local_jdk": { + "bzlFile": "@@rules_java~7.1.0//toolchains:local_java_repository.bzl", + "ruleClassName": "_local_java_repository_rule", + "attributes": { + "name": "rules_java~7.1.0~toolchains~local_jdk", + "java_home": "", + "version": "", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = {RUNTIME_VERSION},\n)\n" + } + }, + "remote_java_tools_darwin_x86_64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remote_java_tools_darwin_x86_64", + "sha256": "0db40d8505a2b65ef0ed46e4256757807db8162f7acff16225be57c1d5726dbc", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.1/java_tools_darwin_x86_64-v13.1.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.1/java_tools_darwin_x86_64-v13.1.zip" + ] + } + }, + "remote_java_tools": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remote_java_tools", + "sha256": "286bdbbd66e616fc4ed3f90101418729a73baa7e8c23a98ffbef558f74c0ad14", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.1/java_tools-v13.1.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.1/java_tools-v13.1.zip" + ] + } + }, + "remotejdk17_linux_s390x": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_linux_s390x", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "ffacba69c6843d7ca70d572489d6cc7ab7ae52c60f0852cedf4cf0d248b6fc37", + "strip_prefix": "jdk-17.0.8.1+1", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz", + "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz" + ] + } + }, + "remotejdk17_win_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk17_win_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_ppc64le": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_linux_ppc64le", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "a8fba686f6eb8ae1d1a9566821dbd5a85a1108b96ad857fdbac5c1e4649fc56f", + "strip_prefix": "jdk-11.0.15+10", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz", + "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz" + ] + } + }, + "remotejdk11_macos_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk11_macos_aarch64", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "7632bc29f8a4b7d492b93f3bc75a7b61630894db85d136456035ab2a24d38885", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz" + ] + } + }, + "remotejdk21_win_toolchain_config_repo": { + "bzlFile": "@@rules_java~7.1.0//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "name": "rules_java~7.1.0~toolchains~remotejdk21_win_toolchain_config_repo", + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_win//:jdk\",\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_java~7.1.0", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_java~7.1.0", + "remote_java_tools", + "rules_java~7.1.0~toolchains~remote_java_tools" + ] + ] + } + }, + "@@rules_python~0.31.0//python/extensions:python.bzl%python": { + "general": { + "bzlTransitiveDigest": "nfcQ92K2B0JOoUwqlGTKoGF7+XoHjDW/y8t8LMG8TE4=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "python_3_11_s390x-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_s390x-unknown-linux-gnu", + "sha256": "49520e3ff494708020f306e30b0964f079170be83e956be4504f850557378a22", + "patches": [], + "platform": "s390x-unknown-linux-gnu", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-s390x-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-s390x-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_11_host": { + "bzlFile": "@@rules_python~0.31.0//python/private:toolchains_repo.bzl", + "ruleClassName": "host_toolchain", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_host", + "python_version": "3.11.7", + "user_repository_name": "python_3_11", + "platforms": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "ppc64le-unknown-linux-gnu", + "s390x-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu" + ] + } + }, + "python_3_10_aarch64-apple-darwin": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_aarch64-apple-darwin", + "sha256": "fd027b1dedf1ea034cdaa272e91771bdf75ddef4c8653b05d224a0645aa2ca3c", + "patches": [], + "platform": "aarch64-apple-darwin", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-aarch64-apple-darwin-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-aarch64-apple-darwin-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_10_x86_64-apple-darwin": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_x86_64-apple-darwin", + "sha256": "be0b19b6af1f7d8c667e5abef5505ad06cf72e5a11bb5844970c395a7e5b1275", + "patches": [], + "platform": "x86_64-apple-darwin", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-x86_64-apple-darwin-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-x86_64-apple-darwin-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_11_aarch64-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_aarch64-unknown-linux-gnu", + "sha256": "b102eaf865eb715aa98a8a2ef19037b6cc3ae7dfd4a632802650f29de635aa13", + "patches": [], + "platform": "aarch64-unknown-linux-gnu", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-aarch64-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-aarch64-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_11_aarch64-apple-darwin": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_aarch64-apple-darwin", + "sha256": "b042c966920cf8465385ca3522986b12d745151a72c060991088977ca36d3883", + "patches": [], + "platform": "aarch64-apple-darwin", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-aarch64-apple-darwin-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-aarch64-apple-darwin-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_10_ppc64le-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_ppc64le-unknown-linux-gnu", + "sha256": "f3f9c43eec1a0c3f72845d0b705da17a336d3906b7df212d2640b8f47e8ff375", + "patches": [], + "platform": "ppc64le-unknown-linux-gnu", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-ppc64le-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-ppc64le-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_10_x86_64-pc-windows-msvc": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_x86_64-pc-windows-msvc", + "sha256": "b8d930ce0d04bda83037ad3653d7450f8907c88e24bb8255a29b8dab8930d6f1", + "patches": [], + "platform": "x86_64-pc-windows-msvc", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-x86_64-pc-windows-msvc-shared-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-x86_64-pc-windows-msvc-shared-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "pythons_hub": { + "bzlFile": "@@rules_python~0.31.0//python/private/bzlmod:pythons_hub.bzl", + "ruleClassName": "hub_repo", + "attributes": { + "name": "rules_python~0.31.0~python~pythons_hub", + "default_python_version": "3.10", + "toolchain_prefixes": [ + "_0000_python_3_11_", + "_0001_python_3_10_" + ], + "toolchain_python_versions": [ + "3.11", + "3.10" + ], + "toolchain_set_python_version_constraints": [ + "True", + "False" + ], + "toolchain_user_repository_names": [ + "python_3_11", + "python_3_10" + ] + } + }, + "python_3_11_x86_64-pc-windows-msvc": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_x86_64-pc-windows-msvc", + "sha256": "67077e6fa918e4f4fd60ba169820b00be7c390c497bf9bc9cab2c255ea8e6f3e", + "patches": [], + "platform": "x86_64-pc-windows-msvc", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-x86_64-pc-windows-msvc-shared-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-x86_64-pc-windows-msvc-shared-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_10_aarch64-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_aarch64-unknown-linux-gnu", + "sha256": "8675915ff454ed2f1597e27794bc7df44f5933c26b94aa06af510fe91b58bb97", + "patches": [], + "platform": "aarch64-unknown-linux-gnu", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-aarch64-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-aarch64-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_11": { + "bzlFile": "@@rules_python~0.31.0//python/private:toolchains_repo.bzl", + "ruleClassName": "toolchain_aliases", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11", + "python_version": "3.11.7", + "user_repository_name": "python_3_11", + "platforms": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "ppc64le-unknown-linux-gnu", + "s390x-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu" + ] + } + }, + "python_3_10_s390x-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_s390x-unknown-linux-gnu", + "sha256": "859f6cfe9aedb6e8858892fdc124037e83ab05f28d42a7acd314c6a16d6bd66c", + "patches": [], + "platform": "s390x-unknown-linux-gnu", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-s390x-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-s390x-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_10": { + "bzlFile": "@@rules_python~0.31.0//python/private:toolchains_repo.bzl", + "ruleClassName": "toolchain_aliases", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10", + "python_version": "3.10.13", + "user_repository_name": "python_3_10", + "platforms": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "ppc64le-unknown-linux-gnu", + "s390x-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu" + ] + } + }, + "python_3_11_ppc64le-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_ppc64le-unknown-linux-gnu", + "sha256": "b44e1b74afe75c7b19143413632c4386708ae229117f8f950c2094e9681d34c7", + "patches": [], + "platform": "ppc64le-unknown-linux-gnu", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-ppc64le-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-ppc64le-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_11_x86_64-apple-darwin": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_x86_64-apple-darwin", + "sha256": "a0e615eef1fafdc742da0008425a9030b7ea68a4ae4e73ac557ef27b112836d4", + "patches": [], + "platform": "x86_64-apple-darwin", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-x86_64-apple-darwin-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-x86_64-apple-darwin-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_versions": { + "bzlFile": "@@rules_python~0.31.0//python/private:toolchains_repo.bzl", + "ruleClassName": "multi_toolchain_aliases", + "attributes": { + "name": "rules_python~0.31.0~python~python_versions", + "python_versions": { + "3.10": "python_3_10", + "3.11": "python_3_11" + } + } + }, + "python_3_10_x86_64-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_x86_64-unknown-linux-gnu", + "sha256": "5d0429c67c992da19ba3eb58b3acd0b35ec5e915b8cae9a4aa8ca565c423847a", + "patches": [], + "platform": "x86_64-unknown-linux-gnu", + "python_version": "3.10.13", + "release_filename": "20231002/cpython-3.10.13+20231002-x86_64-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13+20231002-x86_64-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + }, + "python_3_10_host": { + "bzlFile": "@@rules_python~0.31.0//python/private:toolchains_repo.bzl", + "ruleClassName": "host_toolchain", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_10_host", + "python_version": "3.10.13", + "user_repository_name": "python_3_10", + "platforms": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "ppc64le-unknown-linux-gnu", + "s390x-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu" + ] + } + }, + "python_3_11_x86_64-unknown-linux-gnu": { + "bzlFile": "@@rules_python~0.31.0//python:repositories.bzl", + "ruleClassName": "python_repository", + "attributes": { + "name": "rules_python~0.31.0~python~python_3_11_x86_64-unknown-linux-gnu", + "sha256": "4a51ce60007a6facf64e5495f4cf322e311ba9f39a8cd3f3e4c026eae488e140", + "patches": [], + "platform": "x86_64-unknown-linux-gnu", + "python_version": "3.11.7", + "release_filename": "20240107/cpython-3.11.7+20240107-x86_64-unknown-linux-gnu-install_only.tar.gz", + "urls": [ + "https://github.com/indygreg/python-build-standalone/releases/download/20240107/cpython-3.11.7+20240107-x86_64-unknown-linux-gnu-install_only.tar.gz" + ], + "distutils_content": "", + "strip_prefix": "python", + "coverage_tool": "", + "ignore_root_user_error": false + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python~0.31.0", + "bazel_skylib", + "bazel_skylib~1.5.0" + ], + [ + "rules_python~0.31.0", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_python~0.31.0//python/private/bzlmod:internal_deps.bzl%internal_deps": { + "general": { + "bzlTransitiveDigest": "YM6cXp9AuQVARYWBY5VPn25r/wLyW6Lq09HCAiVNngE=", + "accumulatedFileDigests": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "pypi__wheel": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__wheel", + "url": "https://files.pythonhosted.org/packages/b8/8b/31273bf66016be6ad22bb7345c37ff350276cfd46e389a0c2ac5da9d9073/wheel-0.41.2-py3-none-any.whl", + "sha256": "75909db2664838d015e3d9139004ee16711748a52c8f336b52882266540215d8", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__click": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__click", + "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", + "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__importlib_metadata": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__importlib_metadata", + "url": "https://files.pythonhosted.org/packages/cc/37/db7ba97e676af155f5fcb1a35466f446eadc9104e25b83366e8088c9c926/importlib_metadata-6.8.0-py3-none-any.whl", + "sha256": "3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pyproject_hooks": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__pyproject_hooks", + "url": "https://files.pythonhosted.org/packages/d5/ea/9ae603de7fbb3df820b23a70f6aff92bf8c7770043254ad8d2dc9d6bcba4/pyproject_hooks-1.0.0-py3-none-any.whl", + "sha256": "283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pep517": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__pep517", + "url": "https://files.pythonhosted.org/packages/ee/2f/ef63e64e9429111e73d3d6cbee80591672d16f2725e648ebc52096f3d323/pep517-0.13.0-py3-none-any.whl", + "sha256": "4ba4446d80aed5b5eac6509ade100bff3e7943a8489de249654a5ae9b33ee35b", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__packaging": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__packaging", + "url": "https://files.pythonhosted.org/packages/ab/c3/57f0601a2d4fe15de7a553c00adbc901425661bf048f2a22dfc500caf121/packaging-23.1-py3-none-any.whl", + "sha256": "994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip_tools": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__pip_tools", + "url": "https://files.pythonhosted.org/packages/e8/df/47e6267c6b5cdae867adbdd84b437393e6202ce4322de0a5e0b92960e1d6/pip_tools-7.3.0-py3-none-any.whl", + "sha256": "8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__setuptools": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__setuptools", + "url": "https://files.pythonhosted.org/packages/4f/ab/0bcfebdfc3bfa8554b2b2c97a555569c4c1ebc74ea288741ea8326c51906/setuptools-68.1.2-py3-none-any.whl", + "sha256": "3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__zipp": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__zipp", + "url": "https://files.pythonhosted.org/packages/8c/08/d3006317aefe25ea79d3b76c9650afabaf6d63d1c8443b236e7405447503/zipp-3.16.2-py3-none-any.whl", + "sha256": "679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__colorama": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__colorama", + "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", + "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__build": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__build", + "url": "https://files.pythonhosted.org/packages/58/91/17b00d5fac63d3dca605f1b8269ba3c65e98059e1fd99d00283e42a454f0/build-0.10.0-py3-none-any.whl", + "sha256": "af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "rules_python_internal": { + "bzlFile": "@@rules_python~0.31.0//python/private:internal_config_repo.bzl", + "ruleClassName": "internal_config_repo", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~rules_python_internal" + } + }, + "pypi__pip": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__pip", + "url": "https://files.pythonhosted.org/packages/50/c2/e06851e8cc28dcad7c155f4753da8833ac06a5c704c109313b8d5a62968a/pip-23.2.1-py3-none-any.whl", + "sha256": "7ccf472345f20d35bdc9d1841ff5f313260c2c33fe417f48c30ac46cccabf5be", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__installer": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__installer", + "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl", + "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__more_itertools": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__more_itertools", + "url": "https://files.pythonhosted.org/packages/5a/cb/6dce742ea14e47d6f565589e859ad225f2a5de576d7696e0623b784e226b/more_itertools-10.1.0-py3-none-any.whl", + "sha256": "64e0735fcfdc6f3464ea133afe8ea4483b1c5fe3a3d69852e6503b43a0b222e6", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__tomli": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "rules_python~0.31.0~internal_deps~pypi__tomli", + "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", + "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude in /python/pip_install/tools/bazel.py\n # to avoid non-determinism following pip install's behavior.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/* *\",\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python~0.31.0", + "bazel_tools", + "bazel_tools" + ] + ] + } + } + } +} diff --git a/README.md b/README.md index 5fec8d4..7e33b13 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,35 @@ This is a Python-based protobuf parser. It is intended to serve as a reference implementation and is _not_ production-ready. +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/shaldengeki/py_proto/main.svg)](https://results.pre-commit.ci/latest/github/shaldengeki/py_proto/main) ![Build status](https://github.com/shaldengeki/py_proto/actions/workflows/main.yml/badge.svg) + +## Usage + +Right now, the primary way to use this as a library in your Bazelified Python code. + +Mount this repo in your Bazel workspace, then add `@py_proto//src/util:parser` as a dependency: +``` +py_library( + name = "your_python_code", + # ... + deps = [ + "@py_proto//src/util:parser", + ] +) +``` + +Then, in your Python code, use the parser: +```python +from src.util.parser import ParseError, Parser +with open("your.proto", "r") as proto_file: + parsed_proto = Parser.loads(proto_file.read()) + +print(parsed_proto.syntax) +``` + ## Development -We support building & running via Bazel. +We support building & running via Bazel. See the `TODO.md` for what's on the roadmap. ### Bazel diff --git a/TODO.md b/TODO.md index 932562d..2ac2e2c 100644 --- a/TODO.md +++ b/TODO.md @@ -59,22 +59,22 @@ - [ ] Diffs - [x] Normalizing - [ ] Complete diffs - - [ ] Proto file-level diffs - - [ ] Syntax changes - - [ ] Import changes - - [ ] Package changes - - [ ] Option changes + - [x] Proto file-level diffs + - [x] Syntax changes + - [x] Import changes + - [x] Package changes + - [x] Option changes - [ ] Enum-level diffs - - [ ] Additions/removals - - [ ] Option changes - - [ ] Field changes - - [ ] Value changes - - [ ] Option changes + - [x] Additions/removals + - [x] Option changes + - [x] Field changes + - [x] Value changes + - [x] Option changes - [ ] Reserveds changes - [ ] Message-level diffs - - [ ] Additions/removals - - [ ] Option changes - - [ ] Field changes + - [x] Additions/removals + - [x] Option changes + - [x] Field changes - [ ] Reserved changes - [ ] Nested enum changes - [ ] Nested message changes @@ -84,4 +84,20 @@ - [ ] RPC changes - [ ] Additions/removals - [ ] Option changes + - [ ] Comment diffs - [ ] Backwards-compatibility check + - [ ] __eq__ should enforce parent equality + - [ ] Scoping of diffs under containing objects + - [ ] Enum options under enums + - [ ] Enum value changes under enums + - [ ] Enum value option changes under enum values + - [ ] Enum reserved changes under enums + - [ ] Message option changes under messages + - [ ] Message field changes under messages + - [ ] Message reserved changes under messages + - [ ] Message nested enum changes under messages + - [ ] Message nested message changes under messages +- [ ] (Perf) use iterators + - [ ] In parsing + - [ ] In properties +- [ ] Remove Proto* and proto_ from everything diff --git a/WORKSPACE b/WORKSPACE index c1ff813..0976905 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,42 +4,6 @@ workspace( load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -http_archive( - name = "rules_python", - sha256 = "a868059c8c6dd6ad45a205cca04084c652cfe1852e6df2d5aca036f6e5438380", - strip_prefix = "rules_python-0.14.0", - url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.14.0.tar.gz", -) - -load("@rules_python//python:repositories.bzl", "python_register_toolchains") - -python_register_toolchains( - name = "python3_10", - # Available versions are listed in @rules_python//python:versions.bzl. - # We recommend using the same version your team is already standardized on. - python_version = "3.10.6", -) - -load("@python3_10//:defs.bzl", "interpreter") -load("@rules_python//python:pip.bzl", "pip_parse") - -pip_parse( - name = "py_proto_deps", - python_interpreter_target = interpreter, - requirements_lock = "//src:requirements.txt", -) - -http_archive( - name = "bazel_skylib", - sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", - ], -) - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - http_archive( name = "com_google_protobuf", build_file_content = """ diff --git a/src/BUILD.bazel b/src/BUILD.bazel index ac1326a..c11c756 100644 --- a/src/BUILD.bazel +++ b/src/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_python//python:defs.bzl", "py_binary", "py_library") +load("@rules_python//python:defs.bzl", "py_library") py_library( name = "proto_node", @@ -173,12 +173,41 @@ py_library( ":proto_extend", ":proto_extensions", ":proto_identifier", + ":proto_map", + ":proto_message_field", ":proto_node", + ":proto_oneof", ":proto_option", ":proto_reserved", ], ) +py_library( + name = "proto_oneof", + srcs = ["proto_oneof.py"], + visibility = ["//visibility:public"], + deps = [ + ":proto_comment", + ":proto_identifier", + ":proto_map", + ":proto_message_field", + ":proto_node", + ":proto_option", + ], +) + +py_library( + name = "proto_map", + srcs = ["proto_map.py"], + visibility = ["//visibility:public"], + deps = [ + ":proto_identifier", + ":proto_int", + ":proto_message_field", + ":proto_node", + ], +) + py_library( name = "proto_enum", srcs = ["proto_enum.py"], @@ -219,22 +248,9 @@ py_library( srcs = ["proto_file.py"], visibility = ["//visibility:public"], deps = [ - ":proto_import", - ":proto_node", - ":proto_option", - ":proto_package", - ":proto_syntax", - ], -) - -py_library( - name = "parser", - srcs = ["parser.py"], - visibility = ["//visibility:public"], - deps = [ + ":proto_comment", ":proto_enum", ":proto_extend", - ":proto_file", ":proto_import", ":proto_message", ":proto_node", @@ -244,11 +260,3 @@ py_library( ":proto_syntax", ], ) - -py_binary( - name = "parser_binary", - srcs = ["parser.py"], - main = "parser.py", - visibility = ["//visibility:public"], - deps = [":parser"], -) diff --git a/src/parser.py b/src/parser.py deleted file mode 100644 index 8a815f8..0000000 --- a/src/parser.py +++ /dev/null @@ -1,97 +0,0 @@ -import sys - -from src.proto_comment import ProtoMultiLineComment, ProtoSingleLineComment -from src.proto_enum import ProtoEnum -from src.proto_extend import ProtoExtend -from src.proto_file import ProtoFile -from src.proto_import import ProtoImport -from src.proto_message import ProtoMessage -from src.proto_node import ParsedProtoNode -from src.proto_option import ProtoOption -from src.proto_package import ProtoPackage -from src.proto_service import ProtoService -from src.proto_syntax import ProtoSyntax - - -class ParseError(ValueError): - pass - - -class Parser: - @staticmethod - def parse_partial_content(partial_proto_content: str) -> ParsedProtoNode: - node_types = [ - ProtoImport, - ProtoMessage, - ProtoPackage, - ProtoOption, - ProtoEnum, - ProtoExtend, - ProtoService, - ProtoSingleLineComment, - ProtoMultiLineComment, - ] # type: list[type[ProtoImport] | type[ProtoMessage] | type[ProtoPackage] | type[ProtoOption] | type[ProtoEnum] | type[ProtoExtend]] - for node_type in node_types: - try: - match_result = node_type.match(partial_proto_content) - except (ValueError, IndexError, TypeError): - raise ParseError( - f"Could not parse proto content:\n{partial_proto_content}" - ) - if match_result is not None: - return match_result - raise ParseError(f"Could not parse proto content:\n{partial_proto_content}") - - @staticmethod - def parse_syntax_and_preceding_comments( - proto_content: str, - ) -> tuple[ProtoSyntax, list[ProtoSingleLineComment | ProtoMultiLineComment], str]: - # First, parse any preceding comments. - parsed_tree = [] - while True: - for node_type in [ProtoSingleLineComment, ProtoMultiLineComment]: - try: - match_result = node_type.match(proto_content) - except (ValueError, IndexError, TypeError): - raise ParseError(f"Could not parse proto content:\n{proto_content}") - if match_result is not None: - parsed_tree.append(match_result.node) - proto_content = match_result.remaining_source.strip() - break - if match_result is None: - break - - # Next, parse syntax. - try: - match_result = ProtoSyntax.match(proto_content.strip()) - except (ValueError, IndexError, TypeError): - raise ParseError(f"Proto doesn't have parseable syntax:\n{proto_content}") - if match_result is None: - raise ParseError(f"Proto doesn't have parseable syntax:\n{proto_content}") - syntax = match_result.node - proto_content = match_result.remaining_source.strip() - parsed_tree.append(syntax) - - return syntax, parsed_tree, proto_content - - @staticmethod - def loads(proto_content: str) -> ProtoFile: - syntax, parsed_tree, proto_content = Parser.parse_syntax_and_preceding_comments( - proto_content - ) - while proto_content: - # Remove empty statements. - if proto_content.startswith(";"): - proto_content = proto_content[1:].strip() - continue - match_result = Parser.parse_partial_content(proto_content) - parsed_tree.append(match_result.node) - proto_content = match_result.remaining_source.strip() - - return ProtoFile(syntax, parsed_tree) - - -if __name__ == "__main__": - with open(sys.argv[1], "r") as proto_file: - parsed_proto = Parser.loads(proto_file.read()) - print(parsed_proto.serialize()) diff --git a/src/proto_bool.py b/src/proto_bool.py index ef8867a..d61d5a0 100644 --- a/src/proto_bool.py +++ b/src/proto_bool.py @@ -4,8 +4,14 @@ from src.proto_node import ParsedProtoNode, ProtoNode +class ParsedProtoBoolNode(ParsedProtoNode): + node: "ProtoBool" + remaining_source: str + + class ProtoBool(ProtoNode): - def __init__(self, value: bool): + def __init__(self, value: bool, *args, **kwargs): + super().__init__(*args, **kwargs) self.value = value def __bool__(self) -> bool: @@ -24,15 +30,21 @@ def normalize(self) -> "ProtoBool": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoBoolNode"]: if proto_source.startswith("true") and ( len(proto_source) == 4 or proto_source[4] not in ProtoFullIdentifier.ALL ): - return ParsedProtoNode(ProtoBool(True), proto_source[4:].strip()) + return ParsedProtoBoolNode( + ProtoBool(value=True, parent=parent), proto_source[4:].strip() + ) elif proto_source.startswith("false") and ( len(proto_source) == 5 or proto_source[5] not in ProtoFullIdentifier.ALL ): - return ParsedProtoNode(ProtoBool(False), proto_source[5:].strip()) + return ParsedProtoBoolNode( + ProtoBool(value=False, parent=parent), proto_source[5:].strip() + ) return None def serialize(self) -> str: diff --git a/src/proto_comment.py b/src/proto_comment.py index 989803c..bf7b3d7 100644 --- a/src/proto_comment.py +++ b/src/proto_comment.py @@ -1,12 +1,17 @@ import abc from typing import Optional -from src.proto_identifier import ProtoFullIdentifier from src.proto_node import ParsedProtoNode, ProtoNode +class ParsedProtoCommentNode(ParsedProtoNode): + node: "ProtoComment" + remaining_source: str + + class ProtoComment(ProtoNode): - def __init__(self, value: str): + def __init__(self, value: str, *args, **kwargs): + super().__init__(*args, **kwargs) self.value = value def __eq__(self, other) -> bool: @@ -22,19 +27,28 @@ def normalize(self) -> Optional["ProtoComment"]: return None @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoCommentNode"]: return None def serialize(self) -> str: return "" +class ParsedProtoSingleLineCommentNode(ParsedProtoCommentNode): + node: "ProtoSingleLineComment" + remaining_source: str + + class ProtoSingleLineComment(ProtoComment): def __str__(self) -> str: return f"" @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoSingleLineCommentNode"]: if not proto_source.startswith("//"): return None @@ -42,8 +56,8 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: newline_pos = proto_source.find("\n") if newline_pos == -1: newline_pos = len(proto_source) - return ParsedProtoNode( - ProtoSingleLineComment(proto_source[:newline_pos]), + return ParsedProtoSingleLineCommentNode( + ProtoSingleLineComment(value=proto_source[:newline_pos], parent=parent), proto_source[newline_pos + 1 :], ) @@ -51,12 +65,19 @@ def serialize(self) -> str: return f"//{self.value}" +class ParsedProtoMultiLineCommentNode(ParsedProtoCommentNode): + node: "ProtoMultiLineComment" + remaining_source: str + + class ProtoMultiLineComment(ProtoComment): def __str__(self) -> str: return f"" @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoMultiLineCommentNode"]: if not proto_source.startswith("/*"): return None @@ -64,8 +85,10 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: close_comment_pos = proto_source.find("*/") if close_comment_pos == -1: return None - return ParsedProtoNode( - ProtoMultiLineComment(proto_source[:close_comment_pos]), + return ParsedProtoMultiLineCommentNode( + ProtoMultiLineComment( + value=proto_source[:close_comment_pos], parent=parent + ), proto_source[close_comment_pos + 2 :], ) diff --git a/src/proto_constant.py b/src/proto_constant.py index 03ed8e9..464d6ae 100644 --- a/src/proto_constant.py +++ b/src/proto_constant.py @@ -12,12 +12,16 @@ ) +class ParsedProtoConstantNode(ParsedProtoNode): + node: "ProtoConstant" + remaining_source: str + + class ProtoConstant(ProtoNode): - def __init__( - self, - value: ProtoConstantTypes, - ): + def __init__(self, value: ProtoConstantTypes, *args, **kwargs): + super().__init__(*args, **kwargs) self.value = value + self.value.parent = self def __eq__(self, other) -> bool: return self.value == other.value @@ -32,52 +36,61 @@ def normalize(self) -> "ProtoConstant": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: - match = ProtoBool.match(proto_source) + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoConstantNode"]: + match = ProtoBool.match(proto_source=proto_source) if match is not None: - return ParsedProtoNode( - ProtoConstant(match.node), + proto_constant = ProtoConstant(value=match.node, parent=parent) + return ParsedProtoConstantNode( + proto_constant, match.remaining_source.strip(), ) sign = ProtoIntSign.POSITIVE if proto_source.startswith("+") or proto_source.startswith("-"): sign = next(x for x in ProtoIntSign if x.value == proto_source[0]) - match = ProtoInt.match(proto_source[1:]) + proto_int_match = ProtoInt.match(proto_source=proto_source[1:]) else: - match = ProtoInt.match(proto_source) - if match is not None: - match.node.sign = sign - return ParsedProtoNode( - ProtoConstant(match.node), - match.remaining_source.strip(), + proto_int_match = ProtoInt.match(proto_source=proto_source) + if proto_int_match is not None: + proto_constant = ProtoConstant(value=proto_int_match.node, parent=parent) + proto_int_match.node.sign = sign + return ParsedProtoConstantNode( + proto_constant, + proto_int_match.remaining_source.strip(), ) - sign = ProtoFloatSign.POSITIVE + float_sign = ProtoFloatSign.POSITIVE if proto_source.startswith("+") or proto_source.startswith("-"): - sign = next(x for x in ProtoFloatSign if x.value == proto_source[0]) - match = ProtoFloat.match(proto_source[1:]) + float_sign = next(x for x in ProtoFloatSign if x.value == proto_source[0]) + float_match = ProtoFloat.match(proto_source=proto_source[1:]) else: - match = ProtoFloat.match(proto_source) - if match is not None: - match.node.sign = sign - return ParsedProtoNode( - ProtoConstant(match.node), - match.remaining_source.strip(), + float_match = ProtoFloat.match(proto_source=proto_source) + if float_match is not None: + proto_constant = ProtoConstant(value=float_match.node, parent=parent) + float_match.node.sign = float_sign + return ParsedProtoConstantNode( + proto_constant, + float_match.remaining_source.strip(), ) - match = ProtoFullIdentifier.match(proto_source) - if match is not None: - return ParsedProtoNode( - ProtoConstant(match.node), - match.remaining_source.strip(), + identifier_match = ProtoFullIdentifier.match( + proto_source=proto_source, parent=None + ) + if identifier_match is not None: + return ParsedProtoConstantNode( + ProtoConstant(value=identifier_match.node, parent=parent), + identifier_match.remaining_source.strip(), ) - match = ProtoStringLiteral.match(proto_source) - if match is not None: - return ParsedProtoNode( - ProtoConstant(match.node), - match.remaining_source.strip(), + string_literal_match = ProtoStringLiteral.match( + proto_source=proto_source, parent=None + ) + if string_literal_match is not None: + return ParsedProtoConstantNode( + ProtoConstant(value=string_literal_match.node, parent=parent), + string_literal_match.remaining_source.strip(), ) return None diff --git a/src/proto_enum.py b/src/proto_enum.py index ecb03fe..986769a 100644 --- a/src/proto_enum.py +++ b/src/proto_enum.py @@ -1,32 +1,36 @@ -from typing import Optional +from typing import Optional, Sequence from src.proto_comment import ( ProtoComment, ProtoMultiLineComment, ProtoSingleLineComment, ) -from src.proto_identifier import ProtoIdentifier +from src.proto_identifier import ParsedProtoIdentifierNode, ProtoIdentifier from src.proto_int import ProtoInt, ProtoIntSign -from src.proto_node import ParsedProtoNode, ProtoNode -from src.proto_option import ProtoOption +from src.proto_node import ParsedProtoNode, ProtoContainerNode, ProtoNode, ProtoNodeDiff +from src.proto_option import ParsedProtoOptionNode, ProtoOption from src.proto_reserved import ProtoReserved -class ProtoEnumValueOption(ProtoOption): - def __eq__(self, other) -> bool: - return super().__eq__(other) +class ParsedProtoEnumValueOptionNode(ParsedProtoOptionNode): + node: "ProtoEnumValueOption" + remaining_source: str + +class ProtoEnumValueOption(ProtoOption): def __str__(self) -> str: return f"<{self.__class__.__name__} name={self.name}, value={self.value}>" @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoEnumValueOptionNode"]: test_source = "option " + proto_source.strip() + ";" - match = ProtoOption.match(test_source) + match = ProtoOption.match(proto_source=test_source) if match is None: return None - return ParsedProtoNode( - cls(match.node.name, match.node.value), + return ParsedProtoEnumValueOptionNode( + cls(name=match.node.name, value=match.node.value, parent=parent), match.remaining_source.strip(), ) @@ -34,20 +38,32 @@ def serialize(self) -> str: return f"{self.name.serialize()} = {self.value.serialize()}" +class ParsedProtoEnumValueNode(ParsedProtoNode): + node: "ProtoEnumValue" + remaining_source: str + + class ProtoEnumValue(ProtoNode): def __init__( self, identifier: ProtoIdentifier, value: ProtoInt, options: Optional[list[ProtoEnumValueOption]] = None, + *args, + **kwargs, ): + super().__init__(*args, **kwargs) self.identifier = identifier + self.identifier.parent = self self.value = value + self.value.parent = self if options is None: self.options = [] else: self.options = options + for option in self.options: + option.parent = self def __eq__(self, other) -> bool: return ( @@ -62,16 +78,22 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + def __hash__(self) -> int: + return hash(str(self)) + def normalize(self) -> "ProtoEnumValue": return ProtoEnumValue( self.identifier, self.value, - sorted(self.options, key=lambda o: o.name), + sorted(self.options, key=lambda o: str(o.name)), + parent=self.parent, ) @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: - match = ProtoIdentifier.match(proto_source) + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoEnumValueNode"]: + match = ProtoIdentifier.match(proto_source=proto_source) if match is None: raise ValueError(f"Proto has invalid enum value name: {proto_source}") @@ -88,19 +110,19 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: sign = ProtoIntSign.POSITIVE if proto_source.startswith("-"): sign = next(x for x in ProtoIntSign if x.value == proto_source[0]) - match = ProtoInt.match(proto_source[1:]) + int_match = ProtoInt.match(proto_source=proto_source[1:]) else: - match = ProtoInt.match(proto_source) - if match is None: + int_match = ProtoInt.match(proto_source=proto_source) + if int_match is None: raise ValueError( f"Proto has invalid enum value, expecting int: {proto_source}" ) - match.node.sign = sign - enum_value = match.node - proto_source = match.remaining_source.strip() + int_match.node.sign = sign + enum_value = int_match.node + proto_source = int_match.remaining_source.strip() - options = [] + options: list[ProtoEnumValueOption] = [] if proto_source.startswith("["): proto_source = proto_source[1:].strip() end_bracket = proto_source.find("]") @@ -109,16 +131,24 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto has invalid enum value option syntax, cannot find ]: {proto_source}" ) for option_part in proto_source[:end_bracket].strip().split(","): - match = ProtoEnumValueOption.match(option_part.strip()) - if match is None: + proto_enum_value_option_match = ProtoEnumValueOption.match( + proto_source=option_part.strip(), parent=None + ) + if proto_enum_value_option_match is None: raise ValueError( f"Proto has invalid enum value option syntax: {proto_source}" ) - options.append(match.node) + options.append(proto_enum_value_option_match.node) proto_source = proto_source[end_bracket + 1 :].strip() - return ParsedProtoNode( - ProtoEnumValue(enum_value_name, enum_value, options), proto_source.strip() + return ParsedProtoEnumValueNode( + ProtoEnumValue( + identifier=enum_value_name, + value=enum_value, + options=options, + parent=parent, + ), + proto_source.strip(), ) def serialize(self) -> str: @@ -131,14 +161,69 @@ def serialize(self) -> str: serialized_parts.append("]") return " ".join(serialized_parts) + ";" + @staticmethod + def diff( + enum: "ProtoEnum", + before: Optional["ProtoEnumValue"], + after: Optional["ProtoEnumValue"], + ) -> Sequence["ProtoNodeDiff"]: + diffs: list["ProtoNodeDiff"] = [] + # TODO: scope these diffs under ProtoEnumValue + if before is None or after is None: + if after is not None: + diffs.append(ProtoEnumValueAdded(enum, after)) + elif before is not None: + diffs.append(ProtoEnumValueRemoved(enum, before)) + else: + if before.identifier != after.identifier: + diffs.append(ProtoEnumValueNameChanged(enum, before, after.identifier)) + else: + raise ValueError( + f"Don't know how to handle diff between enums whose names aren't identical: {before}, {after}" + ) + + diffs.extend( + ProtoEnumValueOption.diff_sets(before, before.options, after.options) + ) + return diffs + + @staticmethod + def diff_sets( + enum: "ProtoEnum", before: list["ProtoEnumValue"], after: list["ProtoEnumValue"] + ) -> list["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + + before_number_to_enum_values = {int(mf.value): mf for mf in before} + after_number_to_enum_values = {int(mf.value): mf for mf in after} + all_numbers = sorted( + set(before_number_to_enum_values.keys()).union( + set(after_number_to_enum_values.keys()) + ) + ) + for number in all_numbers: + diffs.extend( + ProtoEnumValue.diff( + enum, + before_number_to_enum_values.get(number, None), + after_number_to_enum_values.get(number, None), + ) + ) + + return diffs -class ProtoEnum(ProtoNode): - def __init__(self, name: ProtoIdentifier, nodes: list[ProtoNode]): + +class ProtoEnum(ProtoContainerNode): + def __init__(self, name: ProtoIdentifier, *args, **kwargs): + super().__init__(*args, **kwargs) self.name = name - self.nodes = nodes + self.name.parent = self - def __eq__(self, other) -> bool: - return self.name == other.name and self.nodes == other.nodes + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoEnum) + and super().__eq__(other) + and self.name == other.name + ) def __str__(self) -> str: return f"" @@ -151,38 +236,32 @@ def normalize(self) -> "ProtoEnum": lambda n1: not isinstance(n1, ProtoComment), self.nodes ) return ProtoEnum( - self.name, - sorted(non_comment_nodes, key=lambda n: str(n.normalize())), + name=self.name, + nodes=sorted(non_comment_nodes, key=lambda n: str(n.normalize())), + parent=self.parent, ) - @staticmethod - def parse_partial_content(partial_enum_content: str) -> ParsedProtoNode: - for node_type in ( + @classmethod + def container_types(cls) -> list[type[ProtoNode]]: + return [ ProtoSingleLineComment, ProtoMultiLineComment, ProtoOption, ProtoReserved, ProtoEnumValue, - ): - try: - match_result = node_type.match(partial_enum_content) - except (ValueError, IndexError, TypeError): - raise ValueError( - f"Could not parse partial enum content:\n{partial_enum_content}" - ) - if match_result is not None: - return match_result - raise ValueError( - f"Could not parse partial enum content:\n{partial_enum_content}" - ) + ] @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoIdentifierNode"]: if not proto_source.startswith("enum "): return None proto_source = proto_source[5:] - match = ProtoIdentifier.match(proto_source) + match = ProtoIdentifier.match(proto_source=proto_source) if match is None: raise ValueError(f"Proto has invalid enum name: {proto_source}") @@ -194,28 +273,27 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto has invalid syntax, expecting opening curly brace: {proto_source}" ) - proto_source = proto_source[1:].strip() - parsed_tree = [] - while proto_source: - # Remove empty statements. - if proto_source.startswith(";"): - proto_source = proto_source[1:].strip() - continue - - if proto_source.startswith("}"): - proto_source = proto_source[1:].strip() - break + return ParsedProtoIdentifierNode(enum_name, proto_source[1:].strip()) - match_result = ProtoEnum.parse_partial_content(proto_source) - parsed_tree.append(match_result.node) - proto_source = match_result.remaining_source.strip() - - return ParsedProtoNode(ProtoEnum(enum_name, nodes=parsed_tree), proto_source) + @classmethod + def construct( + cls, + header_match: ParsedProtoNode, + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + assert isinstance(header_match, ParsedProtoIdentifierNode) + return ProtoEnum(name=header_match.node, nodes=contained_nodes, parent=parent) @property def options(self) -> list[ProtoOption]: return [node for node in self.nodes if isinstance(node, ProtoOption)] + @property + def values(self) -> list[ProtoEnumValue]: + return [node for node in self.nodes if isinstance(node, ProtoEnumValue)] + def serialize(self) -> str: serialize_parts = ( [f"enum {self.name.serialize()} {{"] @@ -223,3 +301,135 @@ def serialize(self) -> str: + ["}"] ) return "\n".join(serialize_parts) + + @staticmethod + def diff( + parent: ProtoNode, before: "ProtoEnum", after: "ProtoEnum" + ) -> list["ProtoNodeDiff"]: + if before is None and after is not None: + return [ProtoEnumAdded(parent, after)] + elif before is not None and after is None: + return [ProtoEnumRemoved(parent, before)] + elif before is None and after is None: + return [] + elif before.name != after.name: + return [] + elif before == after: + return [] + diffs: list[ProtoNodeDiff] = [] + # TODO: scope these diffs under ProtoEnum + diffs.extend(ProtoOption.diff_sets(parent, before.options, after.options)) + diffs.extend(ProtoEnumValue.diff_sets(before, before.values, after.values)) + return diffs + + @staticmethod + def diff_sets( + parent: ProtoNode, before: list["ProtoEnum"], after: list["ProtoEnum"] + ) -> list["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + before_names = set(o.name.identifier for o in before) + after_names = set(o.name.identifier for o in after) + for name in before_names - after_names: + diffs.append( + ProtoEnumRemoved( + parent, next(i for i in before if i.name.identifier == name) + ) + ) + for name in after_names - before_names: + diffs.append( + ProtoEnumAdded( + parent, next(i for i in after if i.name.identifier == name) + ) + ) + for name in before_names & after_names: + before_enum = next(i for i in before if i.name.identifier == name) + after_enum = next(i for i in after if i.name.identifier == name) + diffs.extend(ProtoEnum.diff(parent, before_enum, after_enum)) + + return diffs + + +class ProtoEnumDiff(ProtoNodeDiff): + def __init__(self, parent: ProtoNode, enum: ProtoEnum): + self.parent = parent + self.enum = enum + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoEnumDiff) + and self.parent == other.parent + and self.enum == other.enum + ) + + def __str__(self) -> str: + return f"<{self.__class__.__name__} enum={self.enum} parent={self.parent}>" + + +class ProtoEnumAdded(ProtoEnumDiff): + pass + + +class ProtoEnumRemoved(ProtoEnumDiff): + pass + + +class ProtoEnumValueDiff(ProtoNodeDiff): + def __init__(self, enum: "ProtoEnum", enum_value: "ProtoEnumValue"): + self.enum = enum + self.enum_value = enum_value + + def __eq__(self, other: object) -> bool: + return ( + super().__eq__(other) + and isinstance(other, ProtoEnumValueDiff) + and self.enum_value == other.enum_value + ) + + def __str__(self) -> str: + return ( + f"<{self.__class__.__name__} enum={self.enum} enum_value={self.enum_value}>" + ) + + +class ProtoEnumValueAdded(ProtoEnumValueDiff): + pass + + +class ProtoEnumValueRemoved(ProtoEnumValueDiff): + pass + + +class ProtoEnumValueNameChanged(ProtoEnumValueDiff): + def __init__( + self, enum: ProtoEnum, enum_value: ProtoEnumValue, new_name: ProtoIdentifier + ): + super().__init__(enum, enum_value) + self.new_name = new_name + + def __eq__(self, other: object) -> bool: + return ( + super().__eq__(other) + and isinstance(other, ProtoEnumValueNameChanged) + and self.new_name == other.new_name + ) + + def __str__(self) -> str: + return f"" + + +class ProtoEnumValueValueChanged(ProtoEnumValueDiff): + def __init__( + self, enum: ProtoEnum, enum_value: ProtoEnumValue, new_value: ProtoInt + ): + super().__init__(enum, enum_value) + self.new_value = new_value + + def __eq__(self, other: object) -> bool: + return ( + super().__eq__(other) + and isinstance(other, ProtoEnumValueValueChanged) + and self.new_value == other.new_value + ) + + def __str__(self) -> str: + return f"" diff --git a/src/proto_extend.py b/src/proto_extend.py index aff1a39..007a942 100644 --- a/src/proto_extend.py +++ b/src/proto_extend.py @@ -5,18 +5,27 @@ ProtoMultiLineComment, ProtoSingleLineComment, ) -from src.proto_identifier import ProtoEnumOrMessageIdentifier +from src.proto_identifier import ( + ParsedProtoEnumOrMessageIdentifierNode, + ProtoEnumOrMessageIdentifier, +) from src.proto_message_field import ProtoMessageField -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_node import ParsedProtoNode, ProtoContainerNode, ProtoNode -class ProtoExtend(ProtoNode): - def __init__(self, name: ProtoEnumOrMessageIdentifier, nodes: list[ProtoNode]): +class ProtoExtend(ProtoContainerNode): + def __init__( + self, + name: ProtoEnumOrMessageIdentifier, + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) self.name = name - self.nodes = nodes + self.name.parent = self def __eq__(self, other) -> bool: - return self.name == other.name and self.nodes == other.nodes + return super().__eq__(other) and self.name == other.name def __str__(self) -> str: return f"" @@ -30,28 +39,24 @@ def normalize(self) -> "ProtoExtend": ) return ProtoExtend( name=self.name, - nodes=sorted(non_comment_nodes, key=lambda f: int(f.number)), + nodes=sorted(non_comment_nodes, key=lambda f: str(f)), + parent=self.parent, ) - @staticmethod - def parse_partial_content(partial_content: str) -> ParsedProtoNode: - for node_type in ( + @classmethod + def container_types(cls) -> list[type[ProtoNode]]: + return [ ProtoSingleLineComment, ProtoMultiLineComment, ProtoMessageField, - ): - try: - match_result = node_type.match(partial_content) - except (ValueError, IndexError, TypeError): - raise ValueError( - f"Could not parse partial extend content:\n{partial_content}" - ) - if match_result is not None: - return match_result - raise ValueError(f"Could not parse partial extend content:\n{partial_content}") + ] @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoEnumOrMessageIdentifierNode"]: if not proto_source.startswith("extend "): return None @@ -68,23 +73,18 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto extend has invalid syntax, expecting opening curly brace: {proto_source}" ) - proto_source = proto_source[1:].strip() - parsed_tree = [] - while proto_source: - # Remove empty statements. - if proto_source.startswith(";"): - proto_source = proto_source[1:].strip() - continue - - if proto_source.startswith("}"): - proto_source = proto_source[1:].strip() - break + return ParsedProtoEnumOrMessageIdentifierNode(name, proto_source[1:].strip()) - match_result = ProtoExtend.parse_partial_content(proto_source) - parsed_tree.append(match_result.node) - proto_source = match_result.remaining_source.strip() - - return ParsedProtoNode(ProtoExtend(name, nodes=parsed_tree), proto_source) + @classmethod + def construct( + cls, + header_match: ParsedProtoNode, + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + assert isinstance(header_match, ParsedProtoEnumOrMessageIdentifierNode) + return ProtoExtend(name=header_match.node, nodes=contained_nodes, parent=parent) def serialize(self) -> str: serialize_parts = ( diff --git a/src/proto_extensions.py b/src/proto_extensions.py index 2008bff..39ad012 100644 --- a/src/proto_extensions.py +++ b/src/proto_extensions.py @@ -10,8 +10,13 @@ class ProtoExtensions(ProtoNode): def __init__( self, ranges: list[ProtoRange], + *args, + **kwargs, ): + super().__init__(*args, **kwargs) self.ranges = ranges + for range in self.ranges: + range.parent = self def __eq__(self, other) -> bool: return self.ranges == other.ranges @@ -25,12 +30,14 @@ def __repr__(self) -> str: def normalize(self) -> "ProtoExtensions": # sort the ranges. return ProtoExtensions( - sorted(self.ranges, key=lambda r: r.min), - self.quote_type, + ranges=sorted(self.ranges, key=lambda r: int(r.min)), + parent=self.parent, ) @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoNode"]: if not proto_source.startswith("extensions "): return None @@ -53,7 +60,9 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: ranges.append(match.node) proto_source = match.remaining_source - return ParsedProtoNode(ProtoExtensions(ranges), proto_source.strip()) + return ParsedProtoNode( + ProtoExtensions(ranges=ranges, parent=parent), proto_source.strip() + ) def serialize(self) -> str: serialize_parts = ["extensions", ", ".join(r.serialize() for r in self.ranges)] diff --git a/src/proto_file.py b/src/proto_file.py index 3b51fa5..94d793a 100644 --- a/src/proto_file.py +++ b/src/proto_file.py @@ -1,16 +1,60 @@ +from typing import Optional, Sequence + +from src.proto_comment import ( + ProtoComment, + ProtoMultiLineComment, + ProtoSingleLineComment, +) +from src.proto_enum import ProtoEnum +from src.proto_extend import ProtoExtend from src.proto_import import ProtoImport -from src.proto_node import ProtoNode +from src.proto_message import ProtoMessage +from src.proto_node import ParsedProtoNode, ProtoContainerNode, ProtoNode, ProtoNodeDiff from src.proto_option import ProtoOption from src.proto_package import ProtoPackage +from src.proto_service import ProtoService from src.proto_syntax import ProtoSyntax -class ProtoFile: - def __init__(self, syntax: ProtoSyntax, nodes: list[ProtoNode]): +class ParsedProtoFileNode(ParsedProtoNode): + node: "ProtoFile" + remaining_source: str + + +class ProtoFileHeaderNode(ProtoNode): + def __init__( + self, header_nodes: list[ProtoNode], syntax: ProtoSyntax, *args, **kwargs + ): + super().__init__(*args, **kwargs) + self.header_nodes = header_nodes self.syntax = syntax - self.nodes = nodes - if len([node for node in nodes if isinstance(node, ProtoPackage)]) > 1: + @classmethod + def match( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoNode"]: + pass + + def serialize(self) -> str: + raise NotImplementedError + + def normalize(self) -> Optional["ProtoNode"]: + raise NotImplementedError + + +class ParsedProtoFileHeaderNode(ParsedProtoNode): + node: "ProtoFileHeaderNode" + remaining_source: str + + +class ProtoFile(ProtoContainerNode): + def __init__(self, syntax: ProtoSyntax, *args, **kwargs): + super().__init__(*args, **kwargs) + self.syntax = syntax + + if len([node for node in self.nodes if isinstance(node, ProtoPackage)]) > 1: raise ValueError(f"Proto can't have more than one package statement") @property @@ -18,16 +62,118 @@ def imports(self) -> list[ProtoImport]: return [node for node in self.nodes if isinstance(node, ProtoImport)] @property - def package(self) -> ProtoPackage: - return next(node for node in self.nodes if isinstance(node, ProtoPackage)) + def package(self) -> Optional[ProtoPackage]: + try: + return next(node for node in self.nodes if isinstance(node, ProtoPackage)) + except StopIteration: + return None @property def options(self) -> list[ProtoOption]: return [node for node in self.nodes if isinstance(node, ProtoOption)] + @property + def enums(self) -> list[ProtoEnum]: + return [node for node in self.nodes if isinstance(node, ProtoEnum)] + + @property + def messages(self) -> list[ProtoMessage]: + return [node for node in self.nodes if isinstance(node, ProtoMessage)] + + @classmethod + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoNode"]: + syntax, parsed_tree, remaining_source = cls.parse_syntax_and_preceding_comments( + proto_source.strip() + ) + return ParsedProtoFileHeaderNode( + ProtoFileHeaderNode(list(parsed_tree), syntax, parent=parent), + remaining_source.strip(), + ) + + @classmethod + def match_footer( + cls, + proto_source: str, + parent: Optional[ProtoNode] = None, + ) -> Optional[str]: + trimmed_source = proto_source.strip() + if trimmed_source == "": + return "" + return None + + @classmethod + def container_types(cls) -> list[type[ProtoNode]]: + return [ + ProtoImport, + ProtoMessage, + ProtoPackage, + ProtoOption, + ProtoEnum, + ProtoExtend, + ProtoService, + ProtoSingleLineComment, + ProtoMultiLineComment, + ] + + @classmethod + def construct( + cls, + header_match: "ParsedProtoNode", + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + assert isinstance(header_match, ParsedProtoFileHeaderNode) + return cls( + header_match.node.syntax, + header_match.node.header_nodes + contained_nodes, + ) + + @staticmethod + def parse_syntax_and_preceding_comments( + proto_content: str, + ) -> tuple[ProtoSyntax, Sequence[ProtoComment], str]: + # First, parse any preceding comments. + parsed_tree = [] + while True: + for node_type in [ProtoSingleLineComment, ProtoMultiLineComment]: + try: + match_result = node_type.match(proto_content) + except (ValueError, IndexError, TypeError): + raise ValueError(f"Could not parse proto content:\n{proto_content}") + if match_result is not None: + parsed_tree.append(match_result.node) + proto_content = match_result.remaining_source.strip() + break + if match_result is None: + break + + # Next, parse syntax. + try: + syntax_match = ProtoSyntax.match(proto_content.strip()) + except (ValueError, IndexError, TypeError): + raise ValueError(f"Proto doesn't have parseable syntax:\n{proto_content}") + if syntax_match is None: + raise ValueError(f"Proto doesn't have parseable syntax:\n{proto_content}") + syntax = syntax_match.node + proto_content = syntax_match.remaining_source.strip() + + return syntax, parsed_tree, proto_content + + def normalize(self) -> Optional["ProtoNode"]: + normalized_nodes = [n.normalize() for n in self.nodes] + return ProtoFile( + syntax=self.syntax.normalize(), + nodes=[n for n in normalized_nodes if n is not None], + ) + def serialize(self) -> str: - serialized_parts = [] - previous_type = self.syntax.__class__ + serialized_parts = [self.syntax.serialize()] + previous_type: type[ProtoNode] = self.syntax.__class__ for node in self.nodes: # Attempt to group up lines of the same type. if node.__class__ != previous_type: @@ -36,3 +182,13 @@ def serialize(self) -> str: serialized_parts.append(node.serialize()) return "\n".join(serialized_parts) + + def diff(self, other: "ProtoFile") -> Sequence[ProtoNodeDiff]: + diffs: list[ProtoNodeDiff] = [] + diffs.extend(ProtoSyntax.diff(self.syntax, other.syntax)) + diffs.extend(ProtoImport.diff_sets(self.imports, other.imports)) + diffs.extend(ProtoPackage.diff(self.package, other.package)) + diffs.extend(ProtoEnum.diff_sets(self, self.enums, other.enums)) + diffs.extend(ProtoMessage.diff_sets(self, self.messages, other.messages)) + + return [d for d in diffs if d is not None] diff --git a/src/proto_float.py b/src/proto_float.py index 65913ce..f1abca1 100644 --- a/src/proto_float.py +++ b/src/proto_float.py @@ -11,13 +11,19 @@ class ProtoFloatSign(Enum): NEGATIVE = "-" +class ParsedProtoFloatNode(ParsedProtoNode): + node: "ProtoFloat" + remaining_source: str + + class ProtoFloat(ProtoNode): SIGNS = set("+-") DIGITS = ProtoInt.DECIMAL DECIMAL = DIGITS | set(".") EXPONENTIAL = set("eE") - def __init__(self, value: float, sign: ProtoFloatSign): + def __init__(self, value: float, sign: ProtoFloatSign, *args, **kwargs): + super().__init__(*args, **kwargs) self.value = value self.sign = sign @@ -38,15 +44,20 @@ def normalize(self) -> "ProtoFloat": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoFloatNode"]: if proto_source.startswith("inf"): proto_source = proto_source[3:] if proto_source and proto_source[0] in ProtoIdentifier.ALL: raise ValueError( f"Proto has invalid float, invalid post-inf character: {proto_source}" ) - return ParsedProtoNode( - ProtoFloat(float("inf"), ProtoFloatSign.POSITIVE), proto_source.strip() + return ParsedProtoFloatNode( + ProtoFloat( + value=float("inf"), sign=ProtoFloatSign.POSITIVE, parent=parent + ), + proto_source.strip(), ) if proto_source.startswith("nan"): @@ -55,8 +66,11 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: raise ValueError( f"Proto has invalid float, invalid post-nan character: {proto_source}" ) - return ParsedProtoNode( - ProtoFloat(float("nan"), ProtoFloatSign.POSITIVE), proto_source.strip() + return ParsedProtoFloatNode( + ProtoFloat( + value=float("nan"), sign=ProtoFloatSign.POSITIVE, parent=parent + ), + proto_source.strip(), ) if not proto_source[0] in ProtoFloat.DECIMAL: @@ -107,8 +121,9 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: base *= pow(10, sign * int(proto_source[: i + 1])) proto_source = proto_source[i + 1 :] - return ParsedProtoNode( - ProtoFloat(base, ProtoFloatSign.POSITIVE), proto_source.strip() + return ParsedProtoFloatNode( + ProtoFloat(value=base, sign=ProtoFloatSign.POSITIVE, parent=parent), + proto_source.strip(), ) def serialize(self) -> str: diff --git a/src/proto_identifier.py b/src/proto_identifier.py index 6dd6164..dbc93b5 100644 --- a/src/proto_identifier.py +++ b/src/proto_identifier.py @@ -3,12 +3,28 @@ from src.proto_node import ParsedProtoNode, ProtoNode +class ParsedProtoIdentifierNode(ParsedProtoNode): + node: "ProtoIdentifier" + remaining_source: str + + +class ParsedProtoFullIdentifierNode(ParsedProtoIdentifierNode): + node: "ProtoFullIdentifier" + remaining_source: str + + +class ParsedProtoEnumOrMessageIdentifierNode(ParsedProtoIdentifierNode): + node: "ProtoEnumOrMessageIdentifier" + remaining_source: str + + class ProtoIdentifier(ProtoNode): ALPHABETICAL = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") STARTING = ALPHABETICAL | set("_") ALL = STARTING | set("0123456789_") - def __init__(self, identifier: str): + def __init__(self, identifier: str, *args, **kwargs): + super().__init__(*args, **kwargs) self.identifier = identifier def __eq__(self, other) -> bool: @@ -20,20 +36,28 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + def __hash__(self): + return hash(str(self)) + def normalize(self) -> "ProtoIdentifier": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoIdentifierNode"]: if proto_source[0] not in ProtoIdentifier.STARTING: return None for i, c in enumerate(proto_source): if c not in ProtoIdentifier.ALL: - return ParsedProtoNode( - ProtoIdentifier(proto_source[:i]), proto_source[i:] + return ParsedProtoIdentifierNode( + ProtoIdentifier(identifier=proto_source[:i], parent=parent), + proto_source[i:], ) - return ParsedProtoNode(ProtoIdentifier(proto_source), "") + return ParsedProtoIdentifierNode( + ProtoIdentifier(identifier=proto_source, parent=parent), "" + ) def serialize(self) -> str: return self.identifier @@ -44,7 +68,9 @@ class ProtoFullIdentifier(ProtoIdentifier): ALL = ProtoIdentifier.ALL | set(".") @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoFullIdentifierNode"]: if proto_source[0] not in ProtoFullIdentifier.STARTING: return None @@ -58,8 +84,11 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto source has invalid identifier, expecting alphanumeric after .: {proto_source}" ) identifier_parts.append(proto_source[last_part_start:i]) - return ParsedProtoNode( - ProtoFullIdentifier(".".join(identifier_parts)), proto_source[i:] + return ParsedProtoFullIdentifierNode( + ProtoFullIdentifier( + identifier=".".join(identifier_parts), parent=parent + ), + proto_source[i:], ) elif c == ".": identifier_parts.append(proto_source[last_part_start:i]) @@ -71,7 +100,10 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto source has invalid identifier, expecting alphanumeric after .: {proto_source}" ) identifier_parts.append(proto_source[last_part_start:]) - return ParsedProtoNode(ProtoFullIdentifier(".".join(identifier_parts)), "") + return ParsedProtoFullIdentifierNode( + ProtoFullIdentifier(identifier=".".join(identifier_parts), parent=parent), + "", + ) class ProtoEnumOrMessageIdentifier(ProtoIdentifier): @@ -79,19 +111,24 @@ class ProtoEnumOrMessageIdentifier(ProtoIdentifier): ALL = ProtoIdentifier.ALL | set(".") @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoEnumOrMessageIdentifierNode"]: if proto_source[0] == ".": matched_source = proto_source[1:] else: matched_source = proto_source - match = ProtoFullIdentifier.match(matched_source) - if match is not None: - match = ParsedProtoNode( - ProtoEnumOrMessageIdentifier(match.node.identifier), - match.remaining_source, + identifier_match = ProtoFullIdentifier.match(matched_source, parent=parent) + if identifier_match is not None: + match = ParsedProtoEnumOrMessageIdentifierNode( + ProtoEnumOrMessageIdentifier( + identifier=identifier_match.node.identifier, parent=parent + ), + identifier_match.remaining_source, ) if proto_source[0] == ".": match.node.identifier = "." + match.node.identifier - return match + return match + return identifier_match diff --git a/src/proto_import.py b/src/proto_import.py index 1c03c02..31a1fb6 100644 --- a/src/proto_import.py +++ b/src/proto_import.py @@ -1,14 +1,21 @@ from typing import Optional -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_node import ParsedProtoNode, ProtoNode, ProtoNodeDiff from src.proto_string_literal import ProtoStringLiteral class ProtoImport(ProtoNode): def __init__( - self, path: ProtoStringLiteral, weak: bool = False, public: bool = False + self, + path: ProtoStringLiteral, + weak: bool = False, + public: bool = False, + *args, + **kwargs, ): + super().__init__(*args, **kwargs) self.path = path + self.path.parent = self self.weak = weak self.public = public @@ -25,11 +32,20 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + def __dict__(self): + return { + "path": self.path.serialize(), + "weak": self.weak, + "public": self.public, + } + def normalize(self) -> "ProtoImport": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoNode"]: if not proto_source.startswith("import "): return None proto_source = proto_source[7:] @@ -56,7 +72,7 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: ) return ParsedProtoNode( - ProtoImport(match.node, weak=weak, public=public), + ProtoImport(path=match.node, weak=weak, public=public, parent=parent), match.remaining_source[1:].strip(), ) @@ -68,3 +84,59 @@ def serialize(self) -> str: parts.append("public") parts.append(f"{self.path.serialize()};") return " ".join(parts) + + @staticmethod + def diff_sets( + before: list["ProtoImport"], after: list["ProtoImport"] + ) -> list["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + before_names = set(i.path for i in before) + after_names = set(i.path for i in after) + for name in before_names - after_names: + diffs.append(ProtoImportRemoved(next(i for i in before if i.path == name))) + for name in after_names - before_names: + diffs.append(ProtoImportAdded(next(i for i in after if i.path == name))) + for name in before_names & after_names: + before_import = next(i for i in before if i.path == name) + after_import = next(i for i in after if i.path == name) + if before_import.weak and not after_import.weak: + diffs.append(ProtoImportMadeNonWeak(after_import)) + elif not before_import.weak and after_import.weak: + diffs.append(ProtoImportMadeWeak(after_import)) + if before_import.public and not after_import.public: + diffs.append(ProtoImportMadeNonPublic(after_import)) + elif not before_import.public and after_import.public: + diffs.append(ProtoImportMadePublic(after_import)) + + return diffs + + +class ProtoImportAdded(ProtoNodeDiff): + def __init__(self, proto_import: ProtoImport): + self.proto_import = proto_import + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoImportAdded) + and self.proto_import == other.proto_import + ) + + +class ProtoImportRemoved(ProtoImportAdded): + pass + + +class ProtoImportMadeWeak(ProtoImportAdded): + pass + + +class ProtoImportMadeNonWeak(ProtoImportAdded): + pass + + +class ProtoImportMadePublic(ProtoImportAdded): + pass + + +class ProtoImportMadeNonPublic(ProtoImportAdded): + pass diff --git a/src/proto_int.py b/src/proto_int.py index dd1e01e..8bae791 100644 --- a/src/proto_int.py +++ b/src/proto_int.py @@ -10,12 +10,18 @@ class ProtoIntSign(Enum): NEGATIVE = "-" +class ParsedProtoIntNode(ParsedProtoNode): + node: "ProtoInt" + remaining_source: str + + class ProtoInt(ProtoNode): OCTAL = set("01234567") DECIMAL = OCTAL | set("89") HEX = DECIMAL | set("ABCDEFabcdef") - def __init__(self, value: int, sign: ProtoIntSign): + def __init__(self, value: int, sign: ProtoIntSign, *args, **kwargs): + super().__init__(*args, **kwargs) self.value = value self.sign = sign @@ -38,7 +44,9 @@ def normalize(self) -> "ProtoInt": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoIntNode"]: if proto_source[0] not in ProtoInt.DECIMAL: return None @@ -58,8 +66,8 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: value = int(f"0x{proto_source[:i + 1]}", 16) except ValueError: raise ValueError(f"Proto has invalid hex: {proto_source}") - return ParsedProtoNode( - ProtoInt(value, ProtoIntSign.POSITIVE), + return ParsedProtoIntNode( + ProtoInt(value=value, sign=ProtoIntSign.POSITIVE, parent=parent), proto_source[i + 1 :].strip(), ) else: @@ -74,8 +82,8 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: value = int(f"0{proto_source[:i + 1]}", 8) except ValueError: raise ValueError(f"Proto has invalid octal: {proto_source}") - return ParsedProtoNode( - ProtoInt(value, ProtoIntSign.POSITIVE), + return ParsedProtoIntNode( + ProtoInt(value=value, sign=ProtoIntSign.POSITIVE, parent=parent), proto_source[i + 1 :].strip(), ) else: @@ -91,8 +99,8 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: except ValueError: return None - return ParsedProtoNode( - ProtoInt(value, ProtoIntSign.POSITIVE), + return ParsedProtoIntNode( + ProtoInt(value=value, sign=ProtoIntSign.POSITIVE, parent=parent), proto_source[i + 1 :].strip(), ) diff --git a/src/proto_map.py b/src/proto_map.py new file mode 100644 index 0000000..20070f9 --- /dev/null +++ b/src/proto_map.py @@ -0,0 +1,284 @@ +from enum import Enum +from typing import Optional, Sequence + +from src.proto_identifier import ProtoEnumOrMessageIdentifier, ProtoIdentifier +from src.proto_int import ProtoInt +from src.proto_message_field import ProtoMessageFieldOption, ProtoMessageFieldTypesEnum +from src.proto_node import ParsedProtoNode, ProtoNode, ProtoNodeDiff + + +class ProtoMapKeyTypesEnum(Enum): + INT32 = "int32" + INT64 = "int64" + UINT32 = "uint32" + UINT64 = "uint64" + SINT32 = "sint32" + SINT64 = "sint64" + FIXED32 = "fixed32" + FIXED64 = "fixed64" + SFIXED32 = "sfixed32" + SFIXED64 = "sfixed64" + BOOL = "bool" + STRING = "string" + + +ProtoMapValueTypesEnum = ProtoMessageFieldTypesEnum + + +class ProtoMap(ProtoNode): + def __init__( + self, + key_type: ProtoMapKeyTypesEnum, + value_type: ProtoMapValueTypesEnum, + name: ProtoIdentifier, + number: ProtoInt, + enum_or_message_type_name: Optional[ProtoEnumOrMessageIdentifier] = None, + options: Optional[list[ProtoMessageFieldOption]] = None, + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.key_type = key_type + self.value_type = value_type + self.name = name + self.name.parent = self + self.number = number + self.number.parent = self + self.enum_or_message_type_name = enum_or_message_type_name + if self.enum_or_message_type_name is not None: + self.enum_or_message_type_name.parent = self + + if options is None: + options = [] + self.options = options + for option in self.options: + option.parent = self + + def __eq__(self, other) -> bool: + return ( + self.key_type == other.key_type + and self.value_type == other.value_type + and self.name == other.name + and self.number == other.number + and self.enum_or_message_type_name == other.enum_or_message_type_name + and self.options == other.options + ) + + def __str__(self) -> str: + return f"<{self.__class__.__name__} key_type={self.key_type} value_type={self.value_type} name={self.name} number={self.number} enum_or_message_type_name={self.enum_or_message_type_name} options={self.options}>" + + def __repr__(self) -> str: + return str(self) + + def normalize(self) -> "ProtoMap": + return ProtoMap( + parent=self.parent, + key_type=self.key_type, + value_type=self.value_type, + name=self.name, + number=self.number, + enum_or_message_type_name=self.enum_or_message_type_name, + options=sorted(self.options, key=lambda o: str(o.normalize())), + ) + + @classmethod + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoNode"]: + if proto_source.startswith("map "): + proto_source = proto_source[4:].strip() + elif proto_source.startswith("map<"): + proto_source = proto_source[3:].strip() + else: + return None + + # Try to match the map key type. + proto_source = proto_source[1:].strip() + key_type = None + for potential_key_type in ProtoMapKeyTypesEnum: + if proto_source.startswith(potential_key_type.value): + key_type = potential_key_type + proto_source = proto_source[len(potential_key_type.value) :].strip() + break + if key_type is None: + return None + + if not proto_source.startswith(","): + return None + proto_source = proto_source[1:].strip() + + # Next, try to match the map value type. + value_type: Optional[ProtoMapValueTypesEnum] = None + for potential_value_type in ProtoMapValueTypesEnum: + if potential_value_type == ProtoMapValueTypesEnum.ENUM_OR_MESSAGE: + # This is special-cased below. + break + if proto_source.startswith(potential_value_type.value): + value_type = potential_value_type + proto_source = proto_source[len(potential_value_type.value) :].strip() + + # If this is an enum or message type, try to match a name. + enum_or_message_type_name = None + if value_type is None: + # See if this is an enum or message type. + match = ProtoEnumOrMessageIdentifier.match(proto_source) + if match is None: + return None + value_type = ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE + enum_or_message_type_name = match.node + proto_source = match.remaining_source.strip() + + if not proto_source.startswith(">"): + return None + proto_source = proto_source[1:].strip() + + # Try to match the map field's name. + identifier_match = ProtoIdentifier.match(proto_source) + if identifier_match is None: + return None + name = identifier_match.node + proto_source = identifier_match.remaining_source.strip() + + if not proto_source.startswith("="): + return None + proto_source = proto_source[1:].strip() + + # Try to match the map field number. + int_match = ProtoInt.match(proto_source) + if int_match is None: + return None + number = int_match.node + proto_source = int_match.remaining_source.strip() + + # Try to match map field options, if any. + options = [] + if proto_source.startswith("["): + proto_source = proto_source[1:].strip() + end_bracket = proto_source.find("]") + if end_bracket == -1: + raise ValueError( + f"Proto has invalid map field option syntax, cannot find ]: {proto_source}" + ) + for option_part in proto_source[:end_bracket].strip().split(","): + message_field_option_match = ProtoMessageFieldOption.match( + option_part.strip() + ) + if message_field_option_match is None: + raise ValueError( + f"Proto has invalid map field option syntax: {proto_source}" + ) + options.append(message_field_option_match.node) + proto_source = proto_source[end_bracket + 1 :].strip() + + if not proto_source.startswith(";"): + raise ValueError( + f"Proto has invalid map field syntax, missing ending ;:{proto_source}" + ) + + return ParsedProtoNode( + ProtoMap( + key_type=key_type, + value_type=value_type, + name=name, + number=number, + enum_or_message_type_name=enum_or_message_type_name, + options=options, + parent=parent, + ), + proto_source[1:].strip(), + ) + + def serialize(self) -> str: + serialized_parts = [ + f"map", + f"<{self.key_type.value},", + ] + + if self.value_type == ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE: + if self.enum_or_message_type_name is None: + raise ValueError( + f"Enum or message type name was not set for: {str(self)}" + ) + serialized_parts.append(f"{self.enum_or_message_type_name.serialize()}>") + else: + serialized_parts.append(f"{self.value_type.value}>") + + serialized_parts = serialized_parts + [ + self.name.serialize(), + "=", + self.number.serialize(), + ] + + if self.options: + serialized_parts.append("[") + serialized_parts.append( + ", ".join(option.serialize() for option in self.options) + ) + serialized_parts.append("]") + + return " ".join(serialized_parts) + ";" + + @staticmethod + def diff( + parent: Optional[ProtoNode], before: "ProtoMap", after: "ProtoMap" + ) -> list["ProtoNodeDiff"]: + if before is None and after is not None: + return [ProtoMapAdded(parent, after)] + elif before is not None and after is None: + return [ProtoMapRemoved(parent, before)] + elif before is None and after is None: + return [] + elif before.name != after.name: + return [] + elif before == after: + return [] + diffs: list["ProtoNodeDiff"] = [] + return diffs + + @staticmethod + def diff_sets( + parent: Optional[ProtoNode], before: list["ProtoMap"], after: list["ProtoMap"] + ) -> Sequence["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + before_names = set(o.name.identifier for o in before) + after_names = set(o.name.identifier for o in after) + for name in before_names - after_names: + diffs.append( + ProtoMapRemoved( + parent, next(i for i in before if i.name.identifier == name) + ) + ) + for name in after_names - before_names: + diffs.append( + ProtoMapAdded( + parent, next(i for i in after if i.name.identifier == name) + ) + ) + for name in before_names & after_names: + before_enum = next(i for i in before if i.name.identifier == name) + after_enum = next(i for i in after if i.name.identifier == name) + diffs.extend(ProtoMap.diff(parent, before_enum, after_enum)) + + return diffs + + +class ProtoMapDiff(ProtoNodeDiff): + def __init__(self, parent: Optional[ProtoNode], map: ProtoMap): + self.parent = parent + self.map = map + + def __eq__(self, other: object) -> bool: + return isinstance(other, ProtoMapDiff) and self.map == other.map + + def __str__(self) -> str: + return f"<{self.__class__.__name__} parent={self.parent} map={self.map}>" + + +class ProtoMapAdded(ProtoMapDiff): + def __eq__(self, other: object) -> bool: + return super().__eq__(other) and isinstance(other, ProtoMapAdded) + + +class ProtoMapRemoved(ProtoMapDiff): + def __eq__(self, other: object) -> bool: + return super().__eq__(other) and isinstance(other, ProtoMapRemoved) diff --git a/src/proto_message.py b/src/proto_message.py index 3bb2b81..6e261c0 100644 --- a/src/proto_message.py +++ b/src/proto_message.py @@ -1,5 +1,4 @@ -from enum import Enum -from typing import Optional +from typing import Optional, Sequence from src.proto_comment import ( ProtoComment, @@ -9,334 +8,28 @@ from src.proto_enum import ProtoEnum from src.proto_extend import ProtoExtend from src.proto_extensions import ProtoExtensions -from src.proto_identifier import ( - ProtoEnumOrMessageIdentifier, - ProtoFullIdentifier, - ProtoIdentifier, -) -from src.proto_int import ProtoInt -from src.proto_message_field import ( - ProtoMessageField, - ProtoMessageFieldOption, - ProtoMessageFieldTypesEnum, -) -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_identifier import ParsedProtoIdentifierNode, ProtoIdentifier +from src.proto_map import ProtoMap +from src.proto_message_field import ProtoMessageField +from src.proto_node import ParsedProtoNode, ProtoContainerNode, ProtoNode, ProtoNodeDiff +from src.proto_oneof import ProtoOneOf from src.proto_option import ProtoOption from src.proto_reserved import ProtoReserved -ProtoOneOfNodeTypes = ProtoOption | ProtoMessageField - - -class ProtoOneOf(ProtoNode): - def __init__(self, name: ProtoIdentifier, nodes: list[ProtoOneOfNodeTypes]): - self.name = name - self.nodes = nodes - - def __eq__(self, other) -> bool: - return self.name == other.name and self.nodes == other.nodes - - def __str__(self) -> str: - return f"<{self.__class__.__name__} name={self.name}, nodes={self.nodes}>" - - def __repr__(self) -> str: - return str(self) - - def normalize(self) -> "ProtoOneOf": - non_comment_nodes = filter( - lambda n: not isinstance(n, ProtoComment), self.nodes - ) - options = [] - fields = [] - for node in non_comment_nodes: - if isinstance(node, ProtoOption): - options.append(node.normalize()) - elif ( - isinstance(node, ProtoMessageField) - or isinstance(node, ProtoOneOf) - or isinstance(node, ProtoMap) - ): - fields.append(node.normalize()) - else: - raise ValueError( - f"Can't sort message {self} node for normalizing: {node}" - ) - - sorted_nodes_for_normalizing = sorted( - options, key=lambda o: str(o.normalize()) - ) + sorted(fields, key=lambda f: int(f.number)) - - return ProtoOneOf( - self.name, - nodes=sorted_nodes_for_normalizing, - ) - - @staticmethod - def parse_partial_content(partial_oneof_content: str) -> ParsedProtoNode: - for node_type in ( - ProtoMessageField, - ProtoOption, - ProtoSingleLineComment, - ProtoMultiLineComment, - ): - try: - match_result = node_type.match(partial_oneof_content) - except (ValueError, IndexError, TypeError): - raise ValueError( - f"Could not parse partial oneof content:\n{partial_oneof_content}" - ) - if match_result is not None: - return match_result - raise ValueError( - f"Could not parse partial oneof content:\n{partial_oneof_content}" - ) - - @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: - if not proto_source.startswith("oneof "): - return None - - proto_source = proto_source[6:].strip() - - match = ProtoIdentifier.match(proto_source) - if match is None: - raise ValueError( - f"Proto has invalid syntax, expecting identifier for oneof: {proto_source}" - ) - oneof_name = match.node - proto_source = match.remaining_source.strip() - - if not proto_source.startswith("{"): - raise ValueError( - f"Proto has invalid syntax, expecting opening curly brace: {proto_source}" - ) - - proto_source = proto_source[1:].strip() - parsed_tree = [] - while proto_source: - # Remove empty statements. - if proto_source.startswith(";"): - proto_source = proto_source[1:].strip() - continue - - if proto_source.startswith("}"): - proto_source = proto_source[1:].strip() - break - - match_result = ProtoOneOf.parse_partial_content(proto_source) - parsed_tree.append(match_result.node) - proto_source = match_result.remaining_source.strip() - - return ParsedProtoNode(ProtoOneOf(oneof_name, nodes=parsed_tree), proto_source) - - @property - def options(self) -> list[ProtoOption]: - return [node for node in self.nodes if isinstance(node, ProtoOption)] - - def serialize(self) -> str: - serialize_parts = ( - [f"oneof {self.name.serialize()} {{"] - + [n.serialize() for n in self.nodes] - + ["}"] - ) - return "\n".join(serialize_parts) - - -class ProtoMapKeyTypesEnum(Enum): - INT32 = "int32" - INT64 = "int64" - UINT32 = "uint32" - UINT64 = "uint64" - SINT32 = "sint32" - SINT64 = "sint64" - FIXED32 = "fixed32" - FIXED64 = "fixed64" - SFIXED32 = "sfixed32" - SFIXED64 = "sfixed64" - BOOL = "bool" - STRING = "string" - - -ProtoMapValueTypesEnum = ProtoMessageFieldTypesEnum - - -class ProtoMap(ProtoNode): +class ProtoMessage(ProtoContainerNode): def __init__( self, - key_type: ProtoMapKeyTypesEnum, - value_type: ProtoMapValueTypesEnum, name: ProtoIdentifier, - number: ProtoInt, - enum_or_message_type_name: Optional[ProtoFullIdentifier] = None, - options: Optional[list[ProtoMessageFieldOption]] = None, + *args, + **kwargs, ): - self.key_type = key_type - self.value_type = value_type - self.name = name - self.number = number - self.enum_or_message_type_name = enum_or_message_type_name - - if options is None: - options = [] - self.options = options - - def __eq__(self, other) -> bool: - return ( - self.key_type == other.key_type - and self.value_type == other.value_type - and self.name == other.name - and self.number == other.number - and self.enum_or_message_type_name == other.enum_or_message_type_name - and self.options == other.options - ) - - def __str__(self) -> str: - return f"<{self.__class__.__name__} key_type={self.key_type} value_type={self.value_type} name={self.name} number={self.number} enum_or_message_type_name={self.enum_or_message_type_name} options={self.options}>" - - def __repr__(self) -> str: - return str(self) - - def normalize(self) -> "ProtoMap": - return ProtoMap( - key_type=self.key_type, - value_type=self.value_type, - name=self.name, - number=self.number, - enum_or_message_type_name=self.enum_or_message_type_name, - options=sorted(self.options, key=lambda o: str(o.normalize())), - ) - - @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: - if proto_source.startswith("map "): - proto_source = proto_source[4:].strip() - elif proto_source.startswith("map<"): - proto_source = proto_source[3:].strip() - else: - return None - - # Try to match the map key type. - proto_source = proto_source[1:].strip() - key_type = None - for potential_key_type in ProtoMapKeyTypesEnum: - if proto_source.startswith(potential_key_type.value): - key_type = potential_key_type - proto_source = proto_source[len(potential_key_type.value) :].strip() - break - if key_type is None: - return None - - if not proto_source.startswith(","): - return None - proto_source = proto_source[1:].strip() - - # Next, try to match the map value type. - value_type: Optional[ProtoMapValueTypesEnum] = None - for potential_value_type in ProtoMapValueTypesEnum: - if potential_value_type == ProtoMapValueTypesEnum.ENUM_OR_MESSAGE: - # This is special-cased below. - break - if proto_source.startswith(potential_value_type.value): - value_type = potential_value_type - proto_source = proto_source[len(potential_value_type.value) :].strip() - - # If this is an enum or message type, try to match a name. - enum_or_message_type_name = None - if value_type is None: - # See if this is an enum or message type. - match = ProtoEnumOrMessageIdentifier.match(proto_source) - if match is None: - return None - value_type = ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE - enum_or_message_type_name = match.node - proto_source = match.remaining_source.strip() - - if not proto_source.startswith(">"): - return None - proto_source = proto_source[1:].strip() - - # Try to match the map field's name. - match = ProtoIdentifier.match(proto_source) - if match is None: - return None - name = match.node - proto_source = match.remaining_source.strip() - - if not proto_source.startswith("="): - return None - proto_source = proto_source[1:].strip() - - # Try to match the map field number. - match = ProtoInt.match(proto_source) - if match is None: - return None - number = match.node - proto_source = match.remaining_source.strip() - - # Try to match map field options, if any. - options = [] - if proto_source.startswith("["): - proto_source = proto_source[1:].strip() - end_bracket = proto_source.find("]") - if end_bracket == -1: - raise ValueError( - f"Proto has invalid map field option syntax, cannot find ]: {proto_source}" - ) - for option_part in proto_source[:end_bracket].strip().split(","): - match = ProtoMessageFieldOption.match(option_part.strip()) - if match is None: - raise ValueError( - f"Proto has invalid map field option syntax: {proto_source}" - ) - options.append(match.node) - proto_source = proto_source[end_bracket + 1 :].strip() - - if not proto_source.startswith(";"): - raise ValueError( - f"Proto has invalid map field syntax, missing ending ;:{proto_source}" - ) - - return ParsedProtoNode( - ProtoMap( - key_type, value_type, name, number, enum_or_message_type_name, options - ), - proto_source[1:].strip(), - ) - - def serialize(self) -> str: - serialized_parts = [ - f"map", - f"<{self.key_type.value},", - ] - - if self.value_type == ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE: - serialized_parts.append(f"{self.enum_or_message_type_name.serialize()}>") - else: - serialized_parts.append(f"{self.value_type.value}>") - - serialized_parts = serialized_parts + [ - self.name.serialize(), - "=", - self.number.serialize(), - ] - - if self.options: - serialized_parts.append("[") - serialized_parts.append( - ", ".join(option.serialize() for option in self.options) - ) - serialized_parts.append("]") - - return " ".join(serialized_parts) + ";" - - -class ProtoMessage(ProtoNode): - def __init__(self, name: ProtoIdentifier, nodes: list[ProtoNode]): + super().__init__(*args, **kwargs) self.name = name - self.nodes = nodes + self.name.parent = self def __eq__(self, other) -> bool: - return self.name == other.name and self.nodes == other.nodes + return super().__eq__(other) and self.name == other.name def __str__(self) -> str: return f"" @@ -353,20 +46,19 @@ def normalize(self) -> "ProtoMessage": enums = [] messages = [] fields = [] + oneofs = [] reserveds = [] for node in non_comment_nodes: if isinstance(node, ProtoOption): options.append(node.normalize()) elif isinstance(node, ProtoEnum): - options.append(node.normalize()) + enums.append(node.normalize()) elif isinstance(node, ProtoMessage): messages.append(node.normalize()) - elif ( - isinstance(node, ProtoMessageField) - or isinstance(node, ProtoOneOf) - or isinstance(node, ProtoMap) - ): + elif isinstance(node, ProtoMessageField) or isinstance(node, ProtoMap): fields.append(node.normalize()) + elif isinstance(node, ProtoOneOf): + oneofs.append(node.normalize()) elif isinstance(node, ProtoReserved): reserveds.append(node.normalize()) else: @@ -379,43 +71,22 @@ def normalize(self) -> "ProtoMessage": + sorted(enums, key=lambda e: str(e)) + sorted(messages, key=lambda m: str(m)) + sorted(fields, key=lambda f: int(f.number)) - + sorted(reserveds, key=lambda r: (r.min, r.max)) + + sorted(oneofs, key=lambda o: str(o)) + + sorted(reserveds, key=lambda r: int(r.min)) ) return ProtoMessage( name=self.name, nodes=sorted_nodes_for_normalizing, - ) - - @staticmethod - def parse_partial_content(partial_message_content: str) -> ParsedProtoNode: - for node_type in ( - ProtoSingleLineComment, - ProtoMultiLineComment, - ProtoEnum, - ProtoExtend, - ProtoExtensions, - ProtoOption, - ProtoMessage, - ProtoReserved, - ProtoMessageField, - ProtoOneOf, - ProtoMap, - ): - try: - match_result = node_type.match(partial_message_content) - except (ValueError, IndexError, TypeError): - raise ValueError( - f"Could not parse partial message content:\n{partial_message_content}" - ) - if match_result is not None: - return match_result - raise ValueError( - f"Could not parse partial message content:\n{partial_message_content}" + parent=self.parent, ) @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoIdentifierNode"]: if not proto_source.startswith("message "): return None @@ -432,28 +103,53 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto message has invalid syntax, expecting opening curly brace: {proto_source}" ) - proto_source = proto_source[1:].strip() - parsed_tree = [] - while proto_source: - # Remove empty statements. - if proto_source.startswith(";"): - proto_source = proto_source[1:].strip() - continue + return ParsedProtoIdentifierNode(enum_name, proto_source[1:].strip()) - if proto_source.startswith("}"): - proto_source = proto_source[1:].strip() - break - - match_result = ProtoMessage.parse_partial_content(proto_source) - parsed_tree.append(match_result.node) - proto_source = match_result.remaining_source.strip() + @classmethod + def container_types(cls) -> list[type[ProtoNode]]: + return [ + ProtoSingleLineComment, + ProtoMultiLineComment, + ProtoEnum, + ProtoExtend, + ProtoExtensions, + ProtoOption, + ProtoMessage, + ProtoReserved, + ProtoMessageField, + ProtoOneOf, + ProtoMap, + ] - return ParsedProtoNode(ProtoMessage(enum_name, nodes=parsed_tree), proto_source) + @classmethod + def construct( + cls, + header_match: ParsedProtoNode, + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + assert isinstance(header_match, ParsedProtoIdentifierNode) + return ProtoMessage( + name=header_match.node, nodes=contained_nodes, parent=parent + ) @property def options(self) -> list[ProtoOption]: return [node for node in self.nodes if isinstance(node, ProtoOption)] + @property + def maps(self) -> list[ProtoMap]: + return [node for node in self.nodes if isinstance(node, ProtoMap)] + + @property + def message_fields(self) -> list[ProtoMessageField]: + return [node for node in self.nodes if isinstance(node, ProtoMessageField)] + + @property + def oneofs(self) -> list[ProtoOneOf]: + return [node for node in self.nodes if isinstance(node, ProtoOneOf)] + def serialize(self) -> str: serialize_parts = ( [f"message {self.name.serialize()} {{"] @@ -461,3 +157,94 @@ def serialize(self) -> str: + ["}"] ) return "\n".join(serialize_parts) + + @staticmethod + def diff( + parent: ProtoNode, + before: "ProtoMessage", + after: "ProtoMessage", + ) -> Sequence["ProtoNodeDiff"]: + if before is None and after is not None: + return [ProtoMessageAdded(parent, after)] + elif before is not None and after is None: + return [ProtoMessageRemoved(parent, before)] + elif before is None and after is None: + return [] + elif before.name != after.name: + return [] + elif before == after: + return [] + diffs: list[ProtoNodeDiff] = [] + + # TODO: + # ProtoEnum, + # ProtoExtend, + # ProtoExtensions, + # ProtoMessage, + # ProtoReserved, + diffs.extend(ProtoOption.diff_sets(before, before.options, after.options)) + diffs.extend(ProtoOneOf.diff_sets(before, before.oneofs, after.oneofs)) + diffs.extend(ProtoMap.diff_sets(before, before.maps, after.maps)) + diffs.extend( + ProtoMessageField.diff_sets( + before, before.message_fields, after.message_fields + ) + ) + return diffs + + @staticmethod + def diff_sets( + parent: ProtoNode, + before: list["ProtoMessage"], + after: list["ProtoMessage"], + ) -> Sequence["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + before_names = set(o.name.identifier for o in before) + after_names = set(o.name.identifier for o in after) + for name in before_names - after_names: + diffs.append( + ProtoMessageRemoved( + parent, + next(i for i in before if i.name.identifier == name), + ) + ) + for name in after_names - before_names: + diffs.append( + ProtoMessageAdded( + parent, next(i for i in after if i.name.identifier == name) + ) + ) + for name in before_names & after_names: + before_message = next(i for i in before if i.name.identifier == name) + after_message = next(i for i in after if i.name.identifier == name) + diffs.extend(ProtoMessage.diff(parent, before_message, after_message)) + + return diffs + + +class ProtoMessageDiff(ProtoNodeDiff): + def __init__(self, parent: ProtoNode, message: ProtoMessage): + self.parent = parent + self.message = message + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoMessageDiff) + and self.message == other.message + and self.parent == other.parent + ) + + def __str__(self) -> str: + return ( + f"<{self.__class__.__name__} message={self.message} parent={self.parent}>" + ) + + +class ProtoMessageAdded(ProtoMessageDiff): + def __eq__(self, other: object) -> bool: + return super().__eq__(other) and isinstance(other, ProtoMessageAdded) + + +class ProtoMessageRemoved(ProtoMessageDiff): + def __eq__(self, other: object) -> bool: + return super().__eq__(other) and isinstance(other, ProtoMessageRemoved) diff --git a/src/proto_message_field.py b/src/proto_message_field.py index b2cbba0..1046646 100644 --- a/src/proto_message_field.py +++ b/src/proto_message_field.py @@ -1,18 +1,30 @@ from enum import Enum -from typing import Optional - -from src.proto_enum import ProtoEnumValueOption -from src.proto_identifier import ( - ProtoEnumOrMessageIdentifier, - ProtoFullIdentifier, - ProtoIdentifier, -) +from typing import Optional, Sequence + +from src.proto_enum import ParsedProtoEnumValueOptionNode, ProtoEnumValueOption +from src.proto_identifier import ProtoEnumOrMessageIdentifier, ProtoIdentifier from src.proto_int import ProtoInt -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_node import ParsedProtoNode, ProtoNode, ProtoNodeDiff + + +class ParsedProtoMessageFieldOptionNode(ParsedProtoEnumValueOptionNode): + node: "ProtoMessageFieldOption" + remaining_source: str class ProtoMessageFieldOption(ProtoEnumValueOption): - pass + @classmethod + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoMessageFieldOptionNode"]: + match = super().match(proto_source=proto_source, parent=parent) + if match is None: + return None + + return ParsedProtoMessageFieldOptionNode( + match.node, + match.remaining_source.strip(), + ) class ProtoMessageFieldTypesEnum(Enum): @@ -34,6 +46,11 @@ class ProtoMessageFieldTypesEnum(Enum): ENUM_OR_MESSAGE = "enum_or_message" +class ParsedProtoMessageFieldNode(ParsedProtoNode): + node: "ProtoMessageField" + remaining_source: str + + class ProtoMessageField(ProtoNode): def __init__( self, @@ -42,12 +59,17 @@ def __init__( number: ProtoInt, repeated: bool = False, optional: bool = False, - enum_or_message_type_name: Optional[ProtoFullIdentifier] = None, + enum_or_message_type_name: Optional[ProtoEnumOrMessageIdentifier] = None, options: Optional[list[ProtoMessageFieldOption]] = None, + *args, + **kwargs, ): + super().__init__(*args, **kwargs) self.type = type self.name = name + self.name.parent = self self.number = number + self.number.parent = self # Only allow one of repeated or optional to be true. if repeated and optional: @@ -58,10 +80,14 @@ def __init__( self.repeated = repeated self.optional = optional self.enum_or_message_type_name = enum_or_message_type_name + if self.enum_or_message_type_name is not None: + self.enum_or_message_type_name.parent = self if options is None: options = [] self.options = options + for option in self.options: + option.parent = self def __eq__(self, other) -> bool: return ( @@ -88,11 +114,14 @@ def normalize(self) -> "ProtoMessageField": repeated=self.repeated, optional=self.optional, enum_or_message_type_name=self.enum_or_message_type_name, - options=sorted(self.options, key=lambda o: o.name), + options=sorted(self.options, key=lambda o: str(o.name)), + parent=self.parent, ) @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoMessageFieldNode"]: # First, try to match the optional repeated. repeated = False if proto_source.startswith("repeated "): @@ -130,22 +159,22 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: proto_source = match.remaining_source.strip() # Match the field name. - match = ProtoIdentifier.match(proto_source) - if match is None: + identifier_match = ProtoIdentifier.match(proto_source) + if identifier_match is None: return None - name = match.node - proto_source = match.remaining_source.strip() + name = identifier_match.node + proto_source = identifier_match.remaining_source.strip() if not proto_source.startswith("= "): return None proto_source = proto_source[2:].strip() # Match the field number. - match = ProtoInt.match(proto_source) - if match is None: + int_match = ProtoInt.match(proto_source) + if int_match is None: return None - number = match.node - proto_source = match.remaining_source.strip() + number = int_match.node + proto_source = int_match.remaining_source.strip() options = [] if proto_source.startswith("["): @@ -156,12 +185,14 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto has invalid message field option syntax, cannot find ]: {proto_source}" ) for option_part in proto_source[:end_bracket].strip().split(","): - match = ProtoMessageFieldOption.match(option_part.strip()) - if match is None: + message_field_option_match = ProtoMessageFieldOption.match( + option_part.strip() + ) + if message_field_option_match is None: raise ValueError( f"Proto has invalid message field option syntax: {proto_source}" ) - options.append(match.node) + options.append(message_field_option_match.node) proto_source = proto_source[end_bracket + 1 :].strip() if not proto_source.startswith(";"): @@ -169,15 +200,16 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto has invalid message field syntax, missing ending ;:{proto_source}" ) - return ParsedProtoNode( + return ParsedProtoMessageFieldNode( ProtoMessageField( - matched_type, - name, - number, - repeated, - optional, - enum_or_message_type_name, - options, + type=matched_type, + name=name, + number=number, + repeated=repeated, + optional=optional, + enum_or_message_type_name=enum_or_message_type_name, + options=options, + parent=parent, ), proto_source[1:].strip(), ) @@ -188,6 +220,10 @@ def serialize(self) -> str: serialized_parts.append("repeated") if self.type == ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE: + if self.enum_or_message_type_name is None: + raise ValueError( + f"Enum or message type name was not set for: {str(self)}" + ) serialized_parts.append(self.enum_or_message_type_name.serialize()) else: serialized_parts.append(self.type.value) @@ -206,3 +242,101 @@ def serialize(self) -> str: serialized_parts.append("]") return " ".join(serialized_parts) + ";" + + @staticmethod + def diff( + parent: "ProtoNode", + before: Optional["ProtoMessageField"], + after: Optional["ProtoMessageField"], + ) -> Sequence["ProtoNodeDiff"]: + # TODO: scope these diffs under ProtoMessageField + diffs: list["ProtoNodeDiff"] = [] + if before is None or after is None: + if after is not None: + diffs.append(ProtoMessageFieldAdded(parent, after)) + elif before is not None: + diffs.append(ProtoMessageFieldRemoved(parent, before)) + else: + if before.name != after.name: + diffs.append(ProtoMessageFieldNameChanged(parent, before, after.name)) + if before.number != after.number: + raise ValueError( + f"Don't know how to handle diff between message fields whose names are identical: {before}, {after}" + ) + diffs.extend( + ProtoMessageFieldOption.diff_sets(before, before.options, after.options) + ) + return diffs + + @staticmethod + def diff_sets( + parent: "ProtoNode", + before: list["ProtoMessageField"], + after: list["ProtoMessageField"], + ) -> list["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + + before_number_to_fields = {int(mf.number): mf for mf in before} + after_number_to_fields = {int(mf.number): mf for mf in after} + all_numbers = sorted( + set(before_number_to_fields.keys()).union( + set(after_number_to_fields.keys()) + ) + ) + for number in all_numbers: + diffs.extend( + ProtoMessageField.diff( + parent, + before_number_to_fields.get(number, None), + after_number_to_fields.get(number, None), + ) + ) + + return diffs + + +class ProtoMessageFieldDiff(ProtoNodeDiff): + def __init__(self, message: "ProtoNode", message_field: "ProtoMessageField"): + super().__init__() + self.message = message + self.message_field = message_field + + def __eq__(self, other: object) -> bool: + return ( + super().__eq__(other) + and isinstance(other, ProtoMessageFieldDiff) + and self.message == other.message + and self.message_field == other.message_field + ) + + def __str__(self) -> str: + return f"<{self.__class__.__name__} message={self.message} message_field={self.message_field}>" + + +class ProtoMessageFieldAdded(ProtoMessageFieldDiff): + pass + + +class ProtoMessageFieldRemoved(ProtoMessageFieldDiff): + pass + + +class ProtoMessageFieldNameChanged(ProtoMessageFieldDiff): + def __init__( + self, + message: ProtoNode, + message_field: ProtoMessageField, + new_name: ProtoIdentifier, + ): + super().__init__(message, message_field) + self.new_name = new_name + + def __eq__(self, other: object) -> bool: + return ( + super().__eq__(other) + and isinstance(other, ProtoMessageFieldNameChanged) + and self.new_name == other.new_name + ) + + def __str__(self) -> str: + return f"" diff --git a/src/proto_node.py b/src/proto_node.py index de8685f..05f145c 100644 --- a/src/proto_node.py +++ b/src/proto_node.py @@ -1,13 +1,20 @@ import abc -from typing import NamedTuple, Optional +from typing import NamedTuple, Optional, Sequence class ProtoNode(abc.ABC): @classmethod @abc.abstractmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoNode"]: raise NotImplementedError + def __init__(self, parent: Optional["ProtoNode"] = None): + self.parent = parent + @abc.abstractmethod def serialize(self) -> str: raise NotImplementedError @@ -17,6 +24,109 @@ def normalize(self) -> Optional["ProtoNode"]: raise NotImplementedError +class ProtoContainerNode(ProtoNode): + def __init__( + self, + nodes: Sequence[ProtoNode], + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.nodes = nodes + for node in self.nodes: + node.parent = self + + def __eq__(self, other) -> bool: + return self.nodes == other.nodes + + @classmethod + @abc.abstractmethod + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoNode"]: + raise NotImplementedError + + @classmethod + def match_footer( + cls, + proto_source: str, + parent: Optional[ProtoNode] = None, + ) -> Optional[str]: + if proto_source.startswith("}"): + return proto_source[1:].strip() + + return None + + @classmethod + @abc.abstractmethod + def container_types(cls) -> list[type[ProtoNode]]: + raise NotImplementedError + + @classmethod + @abc.abstractmethod + def construct( + cls, + header_match: "ParsedProtoNode", + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + raise NotImplementedError + + @classmethod + def parse_partial_content(cls, partial_content: str) -> "ParsedProtoNode": + for node_type in cls.container_types(): + try: + match_result = node_type.match(partial_content) + except (ValueError, IndexError, TypeError): + raise ValueError(f"Could not parse partial content:\n{partial_content}") + if match_result is not None: + return match_result + raise ValueError(f"Could not parse partial content:\n{partial_content}") + + @classmethod + def match( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoNode"]: + header_match = cls.match_header(proto_source, parent=parent) + if header_match is None: + return None + + proto_source = header_match.remaining_source.strip() + nodes = [] + footer_match: Optional[str] = None + while proto_source: + # Remove empty statements. + if proto_source.startswith(";"): + proto_source = proto_source[1:].strip() + continue + + footer_match = cls.match_footer(proto_source, parent) + if footer_match is not None: + proto_source = footer_match.strip() + break + + match_result = cls.parse_partial_content(proto_source) + nodes.append(match_result.node) + proto_source = match_result.remaining_source.strip() + + if footer_match is None: + footer_match = cls.match_footer(proto_source, parent) + if footer_match is None: + raise ValueError( + f"Footer was not found when matching container node {cls} for remaining proto source {proto_source}" + ) + + return ParsedProtoNode( + cls.construct(header_match, nodes, footer_match, parent=parent), + proto_source.strip(), + ) + + class ParsedProtoNode(NamedTuple): node: ProtoNode remaining_source: str @@ -26,3 +136,11 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + + +class ProtoNodeDiff(abc.ABC): + def __repr__(self) -> str: + return str(self) + + def __hash__(self) -> int: + return hash(str(self)) diff --git a/src/proto_oneof.py b/src/proto_oneof.py new file mode 100644 index 0000000..50493d3 --- /dev/null +++ b/src/proto_oneof.py @@ -0,0 +1,222 @@ +from typing import Optional, Sequence + +from src.proto_comment import ( + ParsedProtoMultiLineCommentNode, + ParsedProtoSingleLineCommentNode, + ProtoComment, + ProtoMultiLineComment, + ProtoSingleLineComment, +) +from src.proto_identifier import ParsedProtoIdentifierNode, ProtoIdentifier +from src.proto_map import ProtoMap +from src.proto_message_field import ParsedProtoMessageFieldNode, ProtoMessageField +from src.proto_node import ParsedProtoNode, ProtoContainerNode, ProtoNode, ProtoNodeDiff +from src.proto_option import ParsedProtoOptionNode, ProtoOption + +ProtoOneOfNodeTypes = ( + ProtoOption | ProtoMessageField | ProtoSingleLineComment | ProtoMultiLineComment +) +ProtoParsedOneOfNodeTypes = ( + ParsedProtoOptionNode + | ParsedProtoMessageFieldNode + | ParsedProtoSingleLineCommentNode + | ParsedProtoMultiLineCommentNode +) + + +class ParsedProtoOneOfNode(ParsedProtoNode): + node: "ProtoOneOf" + remaining_source: str + + +class ProtoOneOf(ProtoContainerNode): + def __init__( + self, + name: ProtoIdentifier, + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.name = name + self.name.parent = self + + def __eq__(self, other) -> bool: + return super().__eq__(other) and self.name == other.name + + def __str__(self) -> str: + return f"<{self.__class__.__name__} name={self.name}, nodes={self.nodes}>" + + def __repr__(self) -> str: + return str(self) + + def normalize(self) -> "ProtoOneOf": + non_comment_nodes = filter( + lambda n: not isinstance(n, ProtoComment), self.nodes + ) + options = [] + fields: list[ProtoMessageField | ProtoMap] = [] + oneofs: list[ProtoOneOf] = [] + for node in non_comment_nodes: + if isinstance(node, ProtoOption): + options.append(node.normalize()) + elif isinstance(node, ProtoMessageField) or isinstance(node, ProtoMap): + fields.append(node.normalize()) + elif isinstance(node, ProtoOneOf): + oneofs.append(node.normalize()) + else: + raise ValueError( + f"Can't sort message {self} node for normalizing: {node}" + ) + + sorted_options = sorted(options, key=lambda o: str(o.normalize())) + sorted_fields = sorted(fields, key=lambda f: int(f.number)) + sorted_oneofs = sorted( + oneofs, + key=lambda x: min(int(f.number) for f in x.message_fields), + ) + + return ProtoOneOf( + name=self.name, + nodes=(sorted_options + sorted_fields + sorted_oneofs), + parent=self.parent, + ) + + @classmethod + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoIdentifierNode"]: + if not proto_source.startswith("oneof "): + return None + + proto_source = proto_source[6:].strip() + + match = ProtoIdentifier.match(proto_source) + if match is None: + raise ValueError( + f"Proto has invalid syntax, expecting identifier for oneof: {proto_source}" + ) + + oneof_name = match.node + proto_source = match.remaining_source.strip() + + if not proto_source.startswith("{"): + raise ValueError( + f"Proto has invalid syntax, expecting opening curly brace: {proto_source}" + ) + + return ParsedProtoIdentifierNode(oneof_name, proto_source[1:].strip()) + + @classmethod + def container_types(cls) -> list[type[ProtoNode]]: + return [ + ProtoMessageField, + ProtoOption, + ProtoSingleLineComment, + ProtoMultiLineComment, + ] + + @classmethod + def construct( + cls, + header_match: ParsedProtoNode, + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + assert isinstance(header_match, ParsedProtoIdentifierNode) + return ProtoOneOf(name=header_match.node, nodes=contained_nodes, parent=parent) + + @property + def options(self) -> list[ProtoOption]: + return [node for node in self.nodes if isinstance(node, ProtoOption)] + + @property + def message_fields(self) -> list[ProtoMessageField]: + return [node for node in self.nodes if isinstance(node, ProtoMessageField)] + + def serialize(self) -> str: + serialize_parts = ( + [f"oneof {self.name.serialize()} {{"] + + [n.serialize() for n in self.nodes] + + ["}"] + ) + return "\n".join(serialize_parts) + + @staticmethod + def diff( + parent: ProtoNode, before: "ProtoOneOf", after: "ProtoOneOf" + ) -> Sequence["ProtoNodeDiff"]: + if before is None and after is not None: + return [ProtoOneOfAdded(parent, after)] + elif before is not None and after is None: + return [ProtoOneOfRemoved(parent, before)] + elif before is None and after is None: + return [] + elif before.name != after.name: + return [] + elif before == after: + return [] + diffs: list[ProtoNodeDiff] = [] + diffs.extend(ProtoOption.diff_sets(before, before.options, after.options)) + diffs.extend( + ProtoMessageField.diff_sets( + before, before.message_fields, after.message_fields + ) + ) + return diffs + + @staticmethod + def diff_sets( + parent: ProtoNode, + before: list["ProtoOneOf"], + after: list["ProtoOneOf"], + ) -> Sequence["ProtoNodeDiff"]: + diffs: list[ProtoNodeDiff] = [] + before_names = set(o.name.identifier for o in before) + after_names = set(o.name.identifier for o in after) + for name in before_names - after_names: + diffs.append( + ProtoOneOfRemoved( + parent, next(i for i in before if i.name.identifier == name) + ) + ) + for name in after_names - before_names: + diffs.append( + ProtoOneOfAdded( + parent, next(i for i in after if i.name.identifier == name) + ) + ) + for name in before_names & after_names: + before_oneof = next(i for i in before if i.name.identifier == name) + after_oneof = next(i for i in after if i.name.identifier == name) + diffs.extend(ProtoOneOf.diff(parent, before_oneof, after_oneof)) + + return diffs + + +class ProtoOneOfDiff(ProtoNodeDiff): + def __init__(self, parent: ProtoNode, oneof: ProtoOneOf): + self.parent = parent + self.oneof = oneof + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoOneOfDiff) + and self.oneof == other.oneof + and self.parent == other.parent + ) + + def __str__(self) -> str: + return f"<{self.__class__.__name__} oneof={self.oneof} parent={self.parent}>" + + +class ProtoOneOfAdded(ProtoOneOfDiff): + def __eq__(self, other: object) -> bool: + return super().__eq__(other) and isinstance(other, ProtoOneOfAdded) + + +class ProtoOneOfRemoved(ProtoOneOfDiff): + def __eq__(self, other: object) -> bool: + return super().__eq__(other) and isinstance(other, ProtoOneOfRemoved) diff --git a/src/proto_option.py b/src/proto_option.py index 000a39c..6ba48d2 100644 --- a/src/proto_option.py +++ b/src/proto_option.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Sequence from src.proto_constant import ProtoConstant from src.proto_identifier import ( @@ -6,13 +6,21 @@ ProtoFullIdentifier, ProtoIdentifier, ) -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_node import ParsedProtoNode, ProtoNode, ProtoNodeDiff + + +class ParsedProtoOptionNode(ParsedProtoNode): + node: "ProtoOption" + remaining_source: str class ProtoOption(ProtoNode): - def __init__(self, name: ProtoIdentifier, value: ProtoConstant): + def __init__(self, name: ProtoIdentifier, value: ProtoConstant, *args, **kwargs): + super().__init__(*args, **kwargs) self.name = name + self.name.parent = self self.value = value + self.value.parent = self def __eq__(self, other) -> bool: return self.name == other.name and self.value == other.value @@ -23,11 +31,16 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + def __hash__(self): + return hash(str(self)) + def normalize(self) -> "ProtoOption": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoOptionNode"]: if not proto_source.startswith("option "): return None proto_source = proto_source[7:] @@ -36,27 +49,36 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: if proto_source.startswith("("): proto_source = proto_source[1:] match = ProtoFullIdentifier.match(proto_source) - if not match or not match.remaining_source.startswith(")"): + if match is None or not match.remaining_source.startswith(")"): # This might be a regular identifier. - match = ProtoIdentifier.match(proto_source) - if not match or not match.remaining_source.startswith(")"): + identifier_match = ProtoIdentifier.match(proto_source) + if ( + not identifier_match + or not identifier_match.remaining_source.startswith(")") + ): raise ValueError( f"Proto has invalid option when expecting ): {proto_source}" ) - name_parts.append(ProtoIdentifier(f"({match.node.identifier})")) + name_parts.append( + ProtoIdentifier(identifier=f"({identifier_match.node.identifier})") + ) + proto_source = identifier_match.remaining_source[1:] else: - name_parts.append(ProtoFullIdentifier(f"({match.node.identifier})")) - - proto_source = match.remaining_source[1:] + name_parts.append( + ProtoFullIdentifier(identifier=f"({match.node.identifier})") + ) + proto_source = match.remaining_source[1:] while True: - match = ProtoEnumOrMessageIdentifier.match(proto_source) - if match is None: - match = ProtoIdentifier.match(proto_source) - if match is None: + identifier_match = ProtoEnumOrMessageIdentifier.match( + proto_source=proto_source + ) + if identifier_match is None: + identifier_match = ProtoIdentifier.match(proto_source=proto_source) + if identifier_match is None: break - name_parts.append(match.node) - proto_source = match.remaining_source + name_parts.append(identifier_match.node) + proto_source = identifier_match.remaining_source proto_source = proto_source.strip() if not proto_source.startswith("="): @@ -64,30 +86,146 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto has invalid option when expecting =: {proto_source}" ) proto_source = proto_source[1:].strip() - match = ProtoConstant.match(proto_source) - if not match: + constant_match = ProtoConstant.match(proto_source) + if constant_match is None: raise ValueError( f"Proto has invalid option when expecting constant: {proto_source}" ) - proto_source = match.remaining_source - if not match.remaining_source.startswith(";"): + proto_source = constant_match.remaining_source + if not constant_match.remaining_source.startswith(";"): raise ValueError( f"Proto has invalid option when expecting ;: {proto_source}" ) + identifier: ProtoFullIdentifier | ProtoIdentifier if len(name_parts) > 1: - identifier = ProtoFullIdentifier("".join(x.identifier for x in name_parts)) + identifier = ProtoFullIdentifier( + identifier="".join(x.identifier for x in name_parts) + ) else: - identifier = ProtoIdentifier(name_parts[0].identifier) + identifier = ProtoIdentifier(identifier=name_parts[0].identifier) + + proto_option = ProtoOption( + name=identifier, + value=constant_match.node, + parent=parent, + ) + identifier.parent = proto_option + constant_match.node.parent = proto_option - return ParsedProtoNode( - ProtoOption( - name=identifier, - value=match.node, - ), + return ParsedProtoOptionNode( + proto_option, proto_source[1:], ) def serialize(self) -> str: return f"option {self.name.serialize()} = {self.value.serialize()};" + + @staticmethod + def diff( + parent: ProtoNode, + before: "ProtoOption", + after: "ProtoOption", + ) -> Sequence["ProtoOptionDiff"]: + if before is None and after is not None: + return [ProtoOptionAdded(parent, after)] + elif before is not None and after is None: + return [ProtoOptionRemoved(parent, before)] + elif before is None and after is None: + return [] + elif before.name != after.name: + return [] + elif before == after: + return [] + return [ProtoOptionValueChanged(parent, before.name, before.value, after.value)] + + @staticmethod + def diff_sets( + parent: ProtoNode, + before: Sequence["ProtoOption"], + after: Sequence["ProtoOption"], + ) -> list["ProtoOptionDiff"]: + diffs: list[ProtoOptionDiff] = [] + before_names = set(o.name.identifier for o in before) + after_names = set(o.name.identifier for o in after) + for name in before_names - after_names: + diffs.append( + ProtoOptionRemoved( + parent, next(i for i in before if i.name.identifier == name) + ) + ) + for name in after_names - before_names: + diffs.append( + ProtoOptionAdded( + parent, next(i for i in after if i.name.identifier == name) + ) + ) + for name in before_names & after_names: + before_option = next(i for i in before if i.name.identifier == name) + after_option = next(i for i in after if i.name.identifier == name) + diffs.extend(ProtoOption.diff(parent, before_option, after_option)) + + return diffs + + +class ProtoOptionDiff(ProtoNodeDiff): + pass + + +class ProtoOptionValueChanged(ProtoOptionDiff): + def __init__( + self, + parent: ProtoNode, + name: ProtoIdentifier, + before: ProtoConstant, + after: ProtoConstant, + ): + self.name = name + self.before = before + self.after = after + self.parent = parent + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoOptionValueChanged) + and self.name == other.name + and self.before == other.before + and self.after == other.after + and self.parent == other.parent + ) + + def __str__(self) -> str: + return f"" + + +class ProtoOptionAdded(ProtoOptionDiff): + def __init__(self, parent: ProtoNode, before: ProtoOption): + self.parent = parent + self.before = before + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoOptionAdded) + and self.before == other.before + and self.parent == other.parent + ) + + def __str__(self) -> str: + return f"" + + +class ProtoOptionRemoved(ProtoOptionDiff): + def __init__(self, parent: ProtoNode, after: ProtoOption): + self.parent = parent + self.after = after + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoOptionRemoved) + and self.after == other.after + and self.parent == other.parent + ) + + def __str__(self) -> str: + return f"" diff --git a/src/proto_package.py b/src/proto_package.py index d44d5ee..64eb120 100644 --- a/src/proto_package.py +++ b/src/proto_package.py @@ -1,10 +1,11 @@ from typing import Optional -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_node import ParsedProtoNode, ProtoNode, ProtoNodeDiff class ProtoPackage(ProtoNode): - def __init__(self, package: str): + def __init__(self, package: str, *args, **kwargs): + super().__init__(*args, **kwargs) self.package = package def __eq__(self, other) -> bool: @@ -20,7 +21,9 @@ def normalize(self) -> "ProtoPackage": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoNode"]: if not proto_source.startswith("package"): return None @@ -44,7 +47,52 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: if package.startswith(".") or package.endswith("."): raise ValueError(f"Proto has invalid package: {package}") - return ParsedProtoNode(ProtoPackage(package), proto_source.strip()) + return ParsedProtoNode( + ProtoPackage(package=package, parent=parent), proto_source.strip() + ) def serialize(self) -> str: return f"package {self.package};" + + @staticmethod + def diff( + before: Optional["ProtoPackage"], after: Optional["ProtoPackage"] + ) -> list["ProtoNodeDiff"]: + if before == after: + return [] + elif before is not None and after is None: + return [ProtoPackageRemoved(before)] + elif before is None and after is not None: + return [ProtoPackageAdded(after)] + + assert before is not None and after is not None + return [ProtoPackageChanged(before, after)] + + +class ProtoPackageChanged(ProtoNodeDiff): + def __init__(self, before: ProtoPackage, after: ProtoPackage): + self.before = before + self.after = after + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoPackageChanged) + and self.before == other.before + and self.after == other.after + ) + + +class ProtoPackageAdded(ProtoNodeDiff): + def __init__(self, after: ProtoPackage): + self.after = after + + def __eq__(self, other: object) -> bool: + return isinstance(other, ProtoPackageAdded) and self.after == other.after + + +class ProtoPackageRemoved(ProtoNodeDiff): + def __init__(self, before: ProtoPackage): + self.before = before + + def __eq__(self, other: object) -> bool: + return isinstance(other, ProtoPackageRemoved) and self.before == other.before diff --git a/src/proto_range.py b/src/proto_range.py index 2162f79..ce035f9 100644 --- a/src/proto_range.py +++ b/src/proto_range.py @@ -5,12 +5,24 @@ from src.proto_node import ParsedProtoNode, ProtoNode +class ParsedProtoRangeNode(ParsedProtoNode): + node: "ProtoRange" + remaining_source: str + + class ProtoRangeEnum(Enum): MAX = "max" class ProtoRange(ProtoNode): - def __init__(self, min: ProtoInt, max: Optional[ProtoInt | ProtoRangeEnum] = None): + def __init__( + self, + min: ProtoInt, + max: Optional[ProtoInt | ProtoRangeEnum] = None, + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) self.min = min if ( @@ -35,7 +47,9 @@ def normalize(self) -> "ProtoRange": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoRangeNode"]: sign = ProtoIntSign.POSITIVE if proto_source.startswith("-") and proto_source != "-": sign = next(x for x in ProtoIntSign if x.value == proto_source[0]) @@ -53,8 +67,10 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: if proto_source.startswith("to "): proto_source = proto_source[3:] if proto_source.startswith("max"): - return ParsedProtoNode( - ProtoRange(min, ProtoRangeEnum.MAX), + proto_range = ProtoRange(min=min, max=ProtoRangeEnum.MAX, parent=parent) + min.parent = proto_range + return ParsedProtoRangeNode( + proto_range, proto_source[3:].strip(), ) else: @@ -72,7 +88,11 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: max = match.node proto_source = match.remaining_source - return ParsedProtoNode(ProtoRange(min, max), proto_source.strip()) + proto_range = ProtoRange(min=min, max=max, parent=parent) + min.parent = proto_range + if isinstance(max, ProtoNode): + max.parent = proto_range + return ParsedProtoRangeNode(proto_range, proto_source.strip()) def serialize(self) -> str: if self.max is not None: diff --git a/src/proto_reserved.py b/src/proto_reserved.py index 24b40f0..374123c 100644 --- a/src/proto_reserved.py +++ b/src/proto_reserved.py @@ -19,7 +19,10 @@ def __init__( quote_type: Optional[ ProtoReservedFieldQuoteEnum ] = ProtoReservedFieldQuoteEnum.DOUBLE, + *args, + **kwargs, ): + super().__init__(*args, **kwargs) if (not ranges and not fields) or (ranges and fields): raise ValueError( "Exactly one of ranges or fields must be set in a ProtoReserved" @@ -30,11 +33,17 @@ def __init__( if quote_type is None: raise ValueError("Quote type must be specified when reserving fields") + self.ranges = ranges + for range in self.ranges: + range.parent = self + if fields is None: fields = [] - self.ranges = ranges self.fields = fields + for field in self.fields: + field.parent = self + self.quote_type = quote_type def __eq__(self, other) -> bool: @@ -49,13 +58,23 @@ def __repr__(self) -> str: def normalize(self) -> "ProtoReserved": # sort the ranges. return ProtoReserved( - sorted(self.ranges, key=lambda r: r.min), - sorted(self.fields), - self.quote_type, + parent=self.parent, + ranges=sorted(self.ranges, key=lambda r: int(r.min)), + fields=sorted(self.fields, key=lambda f: str(f)), + quote_type=self.quote_type, ) + @property + def min(self) -> str | int: + if self.ranges: + return int(min(self.ranges, key=lambda r: int(r.min)).min) + else: + return str(min(self.fields, key=lambda f: str(f))) + @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoNode"]: if not proto_source.startswith("reserved "): return None @@ -74,10 +93,10 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: ) if proto_source[0] == ",": proto_source = proto_source[1:].strip() - match = ProtoRange.match(proto_source) - if match is not None: - ranges.append(match.node) - proto_source = match.remaining_source + range_match = ProtoRange.match(proto_source) + if range_match is not None: + ranges.append(range_match.node) + proto_source = range_match.remaining_source else: # Maybe this is a field identifier. quote_types = [ @@ -106,7 +125,10 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: proto_source = proto_source[1:].strip() return ParsedProtoNode( - ProtoReserved(ranges, fields, quote_type), proto_source.strip() + ProtoReserved( + ranges=ranges, fields=fields, quote_type=quote_type, parent=parent + ), + proto_source.strip(), ) def serialize(self) -> str: @@ -116,6 +138,7 @@ def serialize(self) -> str: + ", ".join( f"{self.quote_type.value}{f.serialize()}{self.quote_type.value}" for f in self.fields + if self.quote_type is not None ), ] return " ".join(serialize_parts) + ";" diff --git a/src/proto_service.py b/src/proto_service.py index ec00cd3..26f7d17 100644 --- a/src/proto_service.py +++ b/src/proto_service.py @@ -5,8 +5,12 @@ ProtoMultiLineComment, ProtoSingleLineComment, ) -from src.proto_identifier import ProtoEnumOrMessageIdentifier, ProtoIdentifier -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_identifier import ( + ParsedProtoIdentifierNode, + ProtoEnumOrMessageIdentifier, + ProtoIdentifier, +) +from src.proto_node import ParsedProtoNode, ProtoContainerNode, ProtoNode from src.proto_option import ProtoOption @@ -19,16 +23,24 @@ def __init__( request_stream: bool = False, response_stream: bool = False, options: Optional[list[ProtoOption]] = None, + *args, + **kwargs, ): + super().__init__(*args, **kwargs) self.name = name + self.name.parent = self self.request_type = request_type + self.request_type.parent = self self.response_type = response_type + self.response_type.parent = self self.request_stream = request_stream self.response_stream = response_stream if options is None: options = [] self.options = options + for option in self.options: + option.parent = self def __eq__(self, other) -> bool: return ( @@ -54,10 +66,13 @@ def normalize(self) -> "ProtoServiceRPC": request_stream=self.request_stream, response_stream=self.response_stream, options=sorted(self.options, key=lambda o: str(o.normalize())), + parent=self.parent, ) @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoNode"]: if not proto_source.startswith("rpc "): return None proto_source = proto_source[4:].strip() @@ -142,10 +157,9 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: proto_source = proto_source[1:].strip() # Try to parse options. - options = [] + options: list[ProtoOption] = [] if proto_source.startswith("{"): proto_source = proto_source[1:].strip() - options = [] while proto_source: # Remove empty statements. if proto_source.startswith(";"): @@ -168,12 +182,13 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: return ParsedProtoNode( ProtoServiceRPC( - name, - request_name, - response_name, - request_stream, - response_stream, - options, + name=name, + request_type=request_name, + response_type=response_name, + request_stream=request_stream, + response_stream=response_stream, + options=options, + parent=parent, ), proto_source.strip(), ) @@ -207,13 +222,14 @@ def serialize(self) -> str: return " ".join(serialized_parts) + ";" -class ProtoService(ProtoNode): - def __init__(self, name: ProtoIdentifier, nodes: list[ProtoNode]): +class ProtoService(ProtoContainerNode): + def __init__(self, name: ProtoIdentifier, *args, **kwargs): + super().__init__(*args, **kwargs) self.name = name - self.nodes = nodes + self.name.parent = self def __eq__(self, other) -> bool: - return self.name == other.name and self.nodes == other.nodes + return super().__eq__(other) and self.name == other.name def __str__(self) -> str: return f"" @@ -228,30 +244,15 @@ def normalize(self) -> "ProtoService": return ProtoService( name=self.name, nodes=sorted(non_comment_nodes, key=lambda n: str(n.normalize())), - ) - - @staticmethod - def parse_partial_content(partial_service_content: str) -> ParsedProtoNode: - for node_type in ( - ProtoOption, - ProtoServiceRPC, - ProtoSingleLineComment, - ProtoMultiLineComment, - ): - try: - match_result = node_type.match(partial_service_content) - except (ValueError, IndexError, TypeError): - raise ValueError( - f"Could not parse partial service content:\n{partial_service_content}" - ) - if match_result is not None: - return match_result - raise ValueError( - f"Could not parse partial service content:\n{partial_service_content}" + parent=self.parent, ) @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match_header( + cls, + proto_source: str, + parent: Optional["ProtoNode"] = None, + ) -> Optional["ParsedProtoIdentifierNode"]: if not proto_source.startswith("service "): return None @@ -260,7 +261,7 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: if match is None: raise ValueError(f"Proto has invalid service name: {proto_source}") - enum_name = match.node + service_name = match.node proto_source = match.remaining_source.strip() if not proto_source.startswith("{"): @@ -268,23 +269,29 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: f"Proto service has invalid syntax, expecting opening curly brace: {proto_source}" ) - proto_source = proto_source[1:].strip() - parsed_tree = [] - while proto_source: - # Remove empty statements. - if proto_source.startswith(";"): - proto_source = proto_source[1:].strip() - continue - - if proto_source.startswith("}"): - proto_source = proto_source[1:].strip() - break - - match_result = ProtoService.parse_partial_content(proto_source) - parsed_tree.append(match_result.node) - proto_source = match_result.remaining_source.strip() - - return ParsedProtoNode(ProtoService(enum_name, nodes=parsed_tree), proto_source) + return ParsedProtoIdentifierNode(service_name, proto_source[1:].strip()) + + @classmethod + def container_types(cls) -> list[type[ProtoNode]]: + return [ + ProtoOption, + ProtoServiceRPC, + ProtoSingleLineComment, + ProtoMultiLineComment, + ] + + @classmethod + def construct( + cls, + header_match: ParsedProtoNode, + contained_nodes: list[ProtoNode], + footer_match: str, + parent: Optional[ProtoNode] = None, + ) -> ProtoNode: + assert isinstance(header_match, ParsedProtoIdentifierNode) + return ProtoService( + name=header_match.node, nodes=contained_nodes, parent=parent + ) @property def options(self) -> list[ProtoOption]: diff --git a/src/proto_string_literal.py b/src/proto_string_literal.py index b9a4b64..1941cf4 100644 --- a/src/proto_string_literal.py +++ b/src/proto_string_literal.py @@ -3,10 +3,16 @@ from src.proto_node import ParsedProtoNode, ProtoNode +class ParsedProtoStringLiteralNode(ParsedProtoNode): + node: "ProtoStringLiteral" + remaining_source: str + + class ProtoStringLiteral(ProtoNode): QUOTES = ['"', "'"] - def __init__(self, val: str, quote: str = QUOTES[0]): + def __init__(self, val: str, quote: str = QUOTES[0], *args, **kwargs): + super().__init__(*args, **kwargs) self.value = val self.quote = quote @@ -19,11 +25,16 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + def __hash__(self): + return hash(str(self)) + def normalize(self) -> "ProtoStringLiteral": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoStringLiteralNode"]: if not any(proto_source.startswith(c) for c in ProtoStringLiteral.QUOTES): return None escaped = False @@ -33,8 +44,10 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: escaped = True continue if c == starting_quote and not escaped: - return ParsedProtoNode( - ProtoStringLiteral(proto_source[1 : i + 1], quote=starting_quote), + return ParsedProtoStringLiteralNode( + ProtoStringLiteral( + val=proto_source[1 : i + 1], quote=starting_quote, parent=parent + ), proto_source[i + 2 :].strip(), ) escaped = False diff --git a/src/proto_syntax.py b/src/proto_syntax.py index c1f56e8..f2e5576 100644 --- a/src/proto_syntax.py +++ b/src/proto_syntax.py @@ -1,17 +1,23 @@ from enum import Enum from typing import Optional -from src.proto_node import ParsedProtoNode, ProtoNode +from src.proto_node import ParsedProtoNode, ProtoNode, ProtoNodeDiff from src.proto_string_literal import ProtoStringLiteral +class ParsedProtoSyntaxNode(ParsedProtoNode): + node: "ProtoSyntax" + remaining_source: str + + class ProtoSyntaxType(Enum): PROTO2 = "proto2" PROTO3 = "proto3" class ProtoSyntax(ProtoNode): - def __init__(self, syntax: ProtoStringLiteral): + def __init__(self, syntax: ProtoStringLiteral, *args, **kwargs): + super().__init__(*args, **kwargs) self.syntax = syntax def __eq__(self, other) -> bool: @@ -23,11 +29,16 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + def __dict__(self): + return {"syntax": self.syntax.serialize()} + def normalize(self) -> "ProtoSyntax": return self @classmethod - def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: + def match( + cls, proto_source: str, parent: Optional[ProtoNode] = None + ) -> Optional["ParsedProtoSyntaxNode"]: if not proto_source.startswith("syntax = "): return None proto_source = proto_source[9:] @@ -37,16 +48,35 @@ def match(cls, proto_source: str) -> Optional["ParsedProtoNode"]: if not match.remaining_source.startswith(";"): raise ValueError(f"Proto has invalid syntax: {proto_source}") try: - syntax_type = ProtoSyntaxType[match.node.value.upper()] + ProtoSyntaxType[match.node.value.upper()] except KeyError: raise ValueError( f"Proto has unknown syntax type: {match.node.value}, must be one of: {[proto_type.name for proto_type in ProtoSyntaxType]}" ) - return ParsedProtoNode( - ProtoSyntax(match.node), + return ParsedProtoSyntaxNode( + ProtoSyntax(syntax=match.node, parent=parent), match.remaining_source.strip(), ) def serialize(self) -> str: return f"syntax = {self.syntax.serialize()};" + + @staticmethod + def diff(before: "ProtoSyntax", after: "ProtoSyntax") -> list["ProtoNodeDiff"]: + if before == after: + return [] + return [ProtoSyntaxChanged(before, after)] + + +class ProtoSyntaxChanged(ProtoNodeDiff): + def __init__(self, before: ProtoSyntax, after: ProtoSyntax): + self.before = before + self.after = after + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, ProtoSyntaxChanged) + and self.before == other.before + and self.after == other.after + ) diff --git a/src/requirements.txt b/src/requirements.txt index 531c159..ac0152e 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -4,11 +4,11 @@ # # pip-compile src/requirements.txt # -astroid==2.12.13 +astroid==3.0.3 # via # -r src/requirements.txt # pylint -black==22.10.0 +black==24.2.0 # via -r src/requirements.txt cfgv==3.3.1 # via @@ -22,7 +22,7 @@ dill==0.3.6 # via # -r src/requirements.txt # pylint -distlib==0.3.6 +distlib==0.3.8 # via # -r src/requirements.txt # virtualenv @@ -50,11 +50,11 @@ mypy-extensions==0.4.3 # via # -r src/requirements.txt # black -nodeenv==1.7.0 +nodeenv==1.8.0 # via # -r src/requirements.txt # pre-commit -pathspec==0.10.2 +pathspec==0.12.1 # via # -r src/requirements.txt # black diff --git a/src/util/BUILD.bazel b/src/util/BUILD.bazel new file mode 100644 index 0000000..03f3423 --- /dev/null +++ b/src/util/BUILD.bazel @@ -0,0 +1,37 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library") + +py_library( + name = "parser", + srcs = ["parser.py"], + visibility = ["//visibility:public"], + deps = [ + "//src:proto_file", + ], +) + +py_binary( + name = "parser_binary", + srcs = ["parser.py"], + main = "parser.py", + visibility = ["//visibility:public"], + deps = [":parser"], +) + +py_library( + name = "compatibility_checker", + srcs = ["compatibility_checker.py"], + visibility = ["//visibility:public"], + deps = [ + ":parser", + "//src:proto_file", + "//src:proto_node", + ], +) + +py_binary( + name = "compatibility_checker_binary", + srcs = ["compatibility_checker.py"], + main = "compatibility_checker.py", + visibility = ["//visibility:public"], + deps = [":compatibility_checker"], +) diff --git a/src/util/compatibility_checker.py b/src/util/compatibility_checker.py new file mode 100644 index 0000000..45b16f5 --- /dev/null +++ b/src/util/compatibility_checker.py @@ -0,0 +1,40 @@ +import sys +from dataclasses import dataclass +from typing import Type + +from src.proto_file import ProtoFile +from src.proto_message import ProtoMessageAdded +from src.proto_node import ProtoNodeDiff +from src.util.parser import Parser + + +@dataclass +class CompatibilityChecker: + allowed_diff_types: list[Type[ProtoNodeDiff]] + + def check_compatibility(self, before: ProtoFile, after: ProtoFile): + for diff in before.diff(after): + if diff.__class__ in self.allowed_diff_types: + continue + yield diff + + +def main() -> int: + with open(sys.argv[1], "r") as proto_file: + before = Parser.loads(proto_file.read()) + + with open(sys.argv[2], "r") as proto_file: + after = Parser.loads(proto_file.read()) + + violations = list( + CompatibilityChecker([ProtoMessageAdded]).check_compatibility(before, after) + ) + if violations: + print(f"Violations: {violations}") + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/util/parser.py b/src/util/parser.py new file mode 100644 index 0000000..1bd067f --- /dev/null +++ b/src/util/parser.py @@ -0,0 +1,27 @@ +import sys + +from src.proto_file import ProtoFile + + +class ParseError(ValueError): + pass + + +class Parser: + @staticmethod + def loads(proto_content: str) -> ProtoFile: + try: + parsed_file = ProtoFile.match(proto_content, None) + except ValueError as e: + raise ParseError(f"Proto doesn't have parseable syntax:\n{e}") + if parsed_file is None: + raise ParseError(f"Proto doesn't have parseable syntax:\n{proto_content}") + + assert isinstance(parsed_file.node, ProtoFile) + return parsed_file.node + + +if __name__ == "__main__": + with open(sys.argv[1], "r") as proto_file: + parsed_proto = Parser.loads(proto_file.read()) + print(parsed_proto.serialize()) diff --git a/test/BUILD.bazel b/test/BUILD.bazel index ceea7c1..69fd827 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -103,6 +103,7 @@ py_test( srcs = ["proto_import_test.py"], deps = [ "//src:proto_import", + "//src:proto_string_literal", ], ) @@ -124,6 +125,41 @@ py_test( ], ) +py_test( + name = "proto_map_test", + srcs = ["proto_map_test.py"], + deps = [ + "//src:proto_bool", + "//src:proto_comment", + "//src:proto_constant", + "//src:proto_enum", + "//src:proto_extend", + "//src:proto_extensions", + "//src:proto_identifier", + "//src:proto_int", + "//src:proto_map", + "//src:proto_message_field", + "//src:proto_option", + "//src:proto_reserved", + "//src:proto_string_literal", + ], +) + +py_test( + name = "proto_oneof_test", + srcs = ["proto_oneof_test.py"], + deps = [ + "//src:proto_comment", + "//src:proto_constant", + "//src:proto_identifier", + "//src:proto_int", + "//src:proto_message_field", + "//src:proto_oneof", + "//src:proto_option", + "//src:proto_string_literal", + ], +) + py_test( name = "proto_message_test", srcs = ["proto_message_test.py"], @@ -136,7 +172,10 @@ py_test( "//src:proto_extensions", "//src:proto_identifier", "//src:proto_int", + "//src:proto_map", "//src:proto_message", + "//src:proto_message_field", + "//src:proto_oneof", "//src:proto_option", "//src:proto_reserved", "//src:proto_string_literal", @@ -150,7 +189,7 @@ py_test( "//src:proto_extend", "//src:proto_identifier", "//src:proto_int", - "//src:proto_message", + "//src:proto_message_field", ], ) @@ -192,33 +231,9 @@ py_test( ) py_test( - name = "parser_test", - srcs = ["parser_test.py"], + name = "proto_file_test", + srcs = ["proto_file_test.py"], deps = [ - "//src:parser", - "//src:proto_comment", - "//src:proto_constant", - "//src:proto_enum", - "//src:proto_extend", - "//src:proto_extensions", - "//src:proto_float", - "//src:proto_identifier", - "//src:proto_import", - "//src:proto_int", - "//src:proto_message", - "//src:proto_option", - "//src:proto_service", - "//src:proto_string_literal", - "//src:proto_syntax", - ], -) - -sh_test( - name = "parser_binary_test", - srcs = ["parser_binary_test.sh"], - data = [ - "//src:parser_binary", - "//test/resources:all_protos", - "@com_google_protobuf//:all_proto", + "//src:proto_file", ], ) diff --git a/test/proto_constant_test.py b/test/proto_constant_test.py index 2717fa4..fc3fd49 100644 --- a/test/proto_constant_test.py +++ b/test/proto_constant_test.py @@ -18,24 +18,30 @@ def test_ident(self): self.assertEqual(ProtoConstant.match("aa").node.value, ProtoIdentifier("aa")) self.assertEqual(ProtoConstant.match("ab").node.value, ProtoIdentifier("ab")) self.assertEqual( - ProtoConstant.match("a0b_f_aj").node.value, ProtoIdentifier("a0b_f_aj") + ProtoConstant.match("a0b_f_aj").node.value, + ProtoIdentifier("a0b_f_aj"), ) self.assertEqual( - ProtoConstant.match("a.bar").node.value, ProtoIdentifier("a.bar") + ProtoConstant.match("a.bar").node.value, + ProtoIdentifier("a.bar"), ) self.assertEqual( - ProtoConstant.match("a.bar.baz").node.value, ProtoIdentifier("a.bar.baz") + ProtoConstant.match("a.bar.baz").node.value, + ProtoIdentifier("a.bar.baz"), ) def test_str(self): self.assertEqual( - ProtoConstant.match("'a'").node.value, ProtoStringLiteral("a", quote="'") + ProtoConstant.match("'a'").node.value, + ProtoStringLiteral("a", quote="'"), ) self.assertEqual( - ProtoConstant.match("'.a'").node.value, ProtoStringLiteral(".a", quote="'") + ProtoConstant.match("'.a'").node.value, + ProtoStringLiteral(".a", quote="'"), ) self.assertEqual( - ProtoConstant.match('"a"').node.value, ProtoStringLiteral("a", quote='"') + ProtoConstant.match('"a"').node.value, + ProtoStringLiteral("a", quote='"'), ) def test_bool(self): @@ -44,21 +50,24 @@ def test_bool(self): def test_int(self): self.assertEqual( - ProtoConstant.match("1").node.value, ProtoInt(1, ProtoIntSign.POSITIVE) + ProtoConstant.match("1").node.value, + ProtoInt(1, ProtoIntSign.POSITIVE), ) self.assertEqual( ProtoConstant.match("158912938471293847").node.value, ProtoInt(158912938471293847, ProtoIntSign.POSITIVE), ) self.assertEqual( - ProtoConstant.match("+1").node.value, ProtoInt(1, ProtoIntSign.POSITIVE) + ProtoConstant.match("+1").node.value, + ProtoInt(1, ProtoIntSign.POSITIVE), ) self.assertEqual( ProtoConstant.match("+158912938471293847").node.value, ProtoInt(158912938471293847, ProtoIntSign.POSITIVE), ) self.assertEqual( - ProtoConstant.match("-1").node.value, ProtoInt(1, ProtoIntSign.NEGATIVE) + ProtoConstant.match("-1").node.value, + ProtoInt(1, ProtoIntSign.NEGATIVE), ) self.assertEqual( ProtoConstant.match("-248713857").node.value, diff --git a/test/proto_enum_test.py b/test/proto_enum_test.py index 8bffd79..3463a6b 100644 --- a/test/proto_enum_test.py +++ b/test/proto_enum_test.py @@ -4,7 +4,16 @@ from src.proto_bool import ProtoBool from src.proto_comment import ProtoMultiLineComment, ProtoSingleLineComment from src.proto_constant import ProtoConstant -from src.proto_enum import ProtoEnum, ProtoEnumValue, ProtoEnumValueOption +from src.proto_enum import ( + ProtoEnum, + ProtoEnumAdded, + ProtoEnumRemoved, + ProtoEnumValue, + ProtoEnumValueAdded, + ProtoEnumValueNameChanged, + ProtoEnumValueOption, + ProtoEnumValueRemoved, +) from src.proto_identifier import ProtoIdentifier from src.proto_int import ProtoInt, ProtoIntSign from src.proto_option import ProtoOption @@ -15,6 +24,7 @@ class EnumTest(unittest.TestCase): maxDiff = None + DEFAULT_PARENT = ProtoEnum(ProtoIdentifier("DefaultParent"), []) def test_enum_all_features(self): parsed_enum_multiple_values = ProtoEnum.match( @@ -35,7 +45,7 @@ def test_enum_all_features(self): FE_VALTWO = 2; } """.strip() - ) + ), ) self.assertEqual( parsed_enum_multiple_values.node.nodes, @@ -48,19 +58,21 @@ def test_enum_all_features(self): ProtoInt(5, ProtoIntSign.POSITIVE), ProtoRangeEnum.MAX, ), - ] + ], ), ProtoEnumValue( ProtoIdentifier("FE_NEGATIVE"), ProtoInt(1, ProtoIntSign.NEGATIVE), [ ProtoEnumValueOption( - ProtoIdentifier("foo"), ProtoConstant(ProtoBool(False)) + ProtoIdentifier("foo"), + ProtoConstant(ProtoBool(False)), ) ], ), ProtoEnumValue( - ProtoIdentifier("FE_UNDEFINED"), ProtoInt(0, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_UNDEFINED"), + ProtoInt(0, ProtoIntSign.POSITIVE), ), ProtoSingleLineComment(" single-line comment"), ProtoOption( @@ -68,7 +80,7 @@ def test_enum_all_features(self): ProtoConstant(ProtoStringLiteral("foobar")), ), ProtoMultiLineComment( - "\n multiple\n line\n comment\n " + "\n multiple\n line\n comment\n ", ), ProtoEnumValue( ProtoIdentifier("FE_VALONE"), @@ -85,10 +97,15 @@ def test_enum_all_features(self): ], ), ProtoReserved( - [], [ProtoIdentifier("FE_RESERVED"), ProtoIdentifier("FE_OLD")] + [], + [ + ProtoIdentifier("FE_RESERVED"), + ProtoIdentifier("FE_OLD"), + ], ), ProtoEnumValue( - ProtoIdentifier("FE_VALTWO"), ProtoInt(2, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALTWO"), + ProtoInt(2, ProtoIntSign.POSITIVE), ), ], ) @@ -128,7 +145,7 @@ def test_empty_enum(self): } """.strip() - ) + ), ) self.assertIsNotNone(parsed_spaced_enum) self.assertEqual(parsed_spaced_enum.node.name, ProtoIdentifier("FooEnum")) @@ -142,7 +159,7 @@ def test_enum_empty_statements(self): ; } """.strip() - ) + ), ) self.assertIsNotNone(empty_statement_enum) self.assertEqual(empty_statement_enum.node.name, ProtoIdentifier("FooEnum")) @@ -156,7 +173,7 @@ def test_enum_optionals(self): option (foo.bar).baz = false; } """.strip() - ) + ), ) self.assertIsNotNone( parsed_enum_with_optionals.node.options, @@ -183,13 +200,14 @@ def test_enum_single_value(self): FE_UNDEFINED = 0; } """.strip() - ) + ), ) self.assertEqual( parsed_enum_single_value.node.nodes, [ ProtoEnumValue( - ProtoIdentifier("FE_UNDEFINED"), ProtoInt(0, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_UNDEFINED"), + ProtoInt(0, ProtoIntSign.POSITIVE), ) ], ) @@ -205,22 +223,26 @@ def test_enum_multiple_values(self): FE_VALTWO = 2; } """.strip() - ) + ), ) self.assertEqual( parsed_enum_multiple_values.node.nodes, [ ProtoEnumValue( - ProtoIdentifier("FE_NEGATIVE"), ProtoInt(1, ProtoIntSign.NEGATIVE) + ProtoIdentifier("FE_NEGATIVE"), + ProtoInt(1, ProtoIntSign.NEGATIVE), ), ProtoEnumValue( - ProtoIdentifier("FE_UNDEFINED"), ProtoInt(0, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_UNDEFINED"), + ProtoInt(0, ProtoIntSign.POSITIVE), ), ProtoEnumValue( - ProtoIdentifier("FE_VALONE"), ProtoInt(1, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALONE"), + ProtoInt(1, ProtoIntSign.POSITIVE), ), ProtoEnumValue( - ProtoIdentifier("FE_VALTWO"), ProtoInt(2, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALTWO"), + ProtoInt(2, ProtoIntSign.POSITIVE), ), ], ) @@ -238,26 +260,30 @@ def test_enum_comments(self): FE_VALTWO = 2; } """.strip() - ) + ), ) self.assertEqual( parsed_enum_multiple_values.node.nodes, [ ProtoEnumValue( - ProtoIdentifier("FE_NEGATIVE"), ProtoInt(1, ProtoIntSign.NEGATIVE) + ProtoIdentifier("FE_NEGATIVE"), + ProtoInt(1, ProtoIntSign.NEGATIVE), ), ProtoSingleLineComment(" test single-line comment"), ProtoEnumValue( - ProtoIdentifier("FE_UNDEFINED"), ProtoInt(0, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_UNDEFINED"), + ProtoInt(0, ProtoIntSign.POSITIVE), ), ProtoMultiLineComment( - " test multiple\n FE_UNUSED = 200;\n line comment " + " test multiple\n FE_UNUSED = 200;\n line comment ", ), ProtoEnumValue( - ProtoIdentifier("FE_VALONE"), ProtoInt(1, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALONE"), + ProtoInt(1, ProtoIntSign.POSITIVE), ), ProtoEnumValue( - ProtoIdentifier("FE_VALTWO"), ProtoInt(2, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALTWO"), + ProtoInt(2, ProtoIntSign.POSITIVE), ), ], ) @@ -275,26 +301,678 @@ def test_enum_normalize_away_comments(self): FE_VALTWO = 2; } """.strip() - ) + ), ).node.normalize() self.assertEqual( parsed_enum_multiple_values.nodes, [ ProtoEnumValue( - ProtoIdentifier("FE_NEGATIVE"), ProtoInt(1, ProtoIntSign.NEGATIVE) + ProtoIdentifier("FE_NEGATIVE"), + ProtoInt(1, ProtoIntSign.NEGATIVE), + ), + ProtoEnumValue( + ProtoIdentifier("FE_UNDEFINED"), + ProtoInt(0, ProtoIntSign.POSITIVE), ), ProtoEnumValue( - ProtoIdentifier("FE_UNDEFINED"), ProtoInt(0, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALONE"), + ProtoInt(1, ProtoIntSign.POSITIVE), ), ProtoEnumValue( - ProtoIdentifier("FE_VALONE"), ProtoInt(1, ProtoIntSign.POSITIVE) + ProtoIdentifier("FE_VALTWO"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ], + ) + + def test_diff_same_enum_returns_empty(self): + pe1 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [], + ) + pe2 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [], + ) + self.assertEqual(ProtoEnum.diff(self.DEFAULT_PARENT, pe1, pe2), []) + + def test_diff_different_enum_name_returns_empty(self): + pe1 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [], + ) + pe2 = ProtoEnum( + ProtoIdentifier("OtherEnum"), + [], + ) + self.assertEqual(ProtoEnum.diff(self.DEFAULT_PARENT, pe1, pe2), []) + + def test_diff_different_enum_value_name_returns_enum_diff(self): + pe1 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ) + pe2 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_KNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ) + self.assertEqual( + [ + ProtoEnumValueNameChanged( + pe1, + pe1.nodes[0], + ProtoIdentifier("ME_KNOWN"), + ) + ], + ProtoEnum.diff(self.DEFAULT_PARENT, pe1, pe2), + ) + + def test_diff_different_enum_value_value_returns_enum_diff(self): + pe1 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ) + pe2 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ) + ], + ) + + diff = ProtoEnum.diff(self.DEFAULT_PARENT, pe1, pe2) + + self.assertIn( + ProtoEnumValueRemoved( + pe1, + pe1.values[0], + ), + diff, + ) + self.assertIn( + ProtoEnumValueAdded( + pe1, + pe2.values[0], + ), + diff, + ) + self.assertEqual(2, len(diff)) + + def test_diff_enum_added(self): + pe1 = None + pe2 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ) + self.assertEqual( + ProtoEnum.diff(self.DEFAULT_PARENT, pe1, pe2), + [ + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), ), + ], + ) + + def test_diff_enum_removed(self): + pe1 = ProtoEnum( + ProtoIdentifier("MyEnum"), + [ ProtoEnumValue( - ProtoIdentifier("FE_VALTWO"), ProtoInt(2, ProtoIntSign.POSITIVE) + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ) + pe2 = None + self.assertEqual( + ProtoEnum.diff(self.DEFAULT_PARENT, pe1, pe2), + [ + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("MyEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("ME_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), ), ], ) + def test_diff_sets_empty_returns_empty(self): + set1 = [] + set2 = [] + self.assertEqual(ProtoEnum.diff_sets(self.DEFAULT_PARENT, set1, set2), []) + + def test_diff_sets_no_change(self): + set1 = [ + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + self.assertEqual(ProtoEnum.diff_sets(self.DEFAULT_PARENT, set1, set1), []) + + def test_diff_sets_all_removed(self): + set1 = [] + set2 = [ + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + diff = ProtoEnum.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_all_added(self): + set1 = [ + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + set2 = [] + diff = ProtoEnum.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_mutually_exclusive(self): + set1 = [ + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + set2 = [ + ProtoEnum( + ProtoIdentifier("FooEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + + diff = ProtoEnum.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("FooEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("BarEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("TagEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + + self.assertEqual(6, len(diff)) + + def test_diff_sets_overlap(self): + set1 = [ + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + set2 = [ + ProtoEnum( + ProtoIdentifier("FooEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnum( + ProtoIdentifier("TagEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ] + + diff = ProtoEnum.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("FooEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + + self.assertIn( + ProtoEnumRemoved( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("TagEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("FooEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("FE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumAdded( + self.DEFAULT_PARENT, + ProtoEnum( + ProtoIdentifier("TagEnum2"), + [ + ProtoEnumValue( + ProtoIdentifier("TE_UNKNOWN2"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ), + diff, + ) + self.assertIn( + ProtoEnumValueNameChanged( + ProtoEnum( + ProtoIdentifier("BarEnum"), + [ + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ) + ], + ), + ProtoEnumValue( + ProtoIdentifier("BE_UNKNOWN"), + ProtoInt(0, ProtoIntSign.POSITIVE), + ), + ProtoIdentifier("BE_UNKNOWN2"), + ), + diff, + ) + self.assertEqual(5, len(diff)) + if __name__ == "__main__": unittest.main() diff --git a/test/proto_extend_test.py b/test/proto_extend_test.py index 41d54fc..176281a 100644 --- a/test/proto_extend_test.py +++ b/test/proto_extend_test.py @@ -4,7 +4,7 @@ from src.proto_extend import ProtoExtend from src.proto_identifier import ProtoEnumOrMessageIdentifier, ProtoIdentifier from src.proto_int import ProtoInt, ProtoIntSign -from src.proto_message import ProtoMessageField, ProtoMessageFieldTypesEnum +from src.proto_message_field import ProtoMessageField, ProtoMessageFieldTypesEnum class ExtendTest(unittest.TestCase): @@ -14,7 +14,8 @@ def test_empty_extend(self): parsed_empty_extend = ProtoExtend.match("""extend FooMessage {}""") self.assertIsNotNone(parsed_empty_extend) self.assertEqual( - parsed_empty_extend.node.name, ProtoEnumOrMessageIdentifier("FooMessage") + parsed_empty_extend.node.name, + ProtoEnumOrMessageIdentifier("FooMessage"), ) self.assertEqual(parsed_empty_extend.node.serialize(), "extend FooMessage {\n}") @@ -25,11 +26,12 @@ def test_empty_extend(self): } """.strip() - ) + ), ) self.assertIsNotNone(parsed_spaced_extend) self.assertEqual( - parsed_spaced_extend.node.name, ProtoEnumOrMessageIdentifier("FooMessage") + parsed_spaced_extend.node.name, + ProtoEnumOrMessageIdentifier("FooMessage"), ) self.assertEqual( parsed_spaced_extend.node.serialize(), "extend FooMessage {\n}" @@ -42,7 +44,7 @@ def test_empty_extend(self): } """.strip() - ) + ), ) self.assertIsNotNone(parsed_scoped_extend) self.assertEqual( @@ -63,7 +65,7 @@ def test_extend_empty_statements(self): ; } """.strip() - ) + ), ) self.assertIsNotNone(empty_statement_message) self.assertEqual( @@ -82,7 +84,7 @@ def test_extend_simple_field(self): string single_field = 1; } """.strip() - ) + ), ) self.assertEqual( parsed_extend_with_single_field.node, diff --git a/test/proto_extensions_test.py b/test/proto_extensions_test.py index 2e846a2..50f0723 100644 --- a/test/proto_extensions_test.py +++ b/test/proto_extensions_test.py @@ -6,10 +6,12 @@ class ExtensionsTest(unittest.TestCase): def test_extension_single_int(self): self.assertEqual( - ProtoExtensions.match("extensions 21;").node.serialize(), "extensions 21;" + ProtoExtensions.match("extensions 21;").node.serialize(), + "extensions 21;", ) self.assertEqual( - ProtoExtensions.match("extensions -1;").node.serialize(), "extensions -1;" + ProtoExtensions.match("extensions -1;").node.serialize(), + "extensions -1;", ) def test_extension_multiple_ints(self): diff --git a/test/proto_file_test.py b/test/proto_file_test.py new file mode 100644 index 0000000..1bbf1cd --- /dev/null +++ b/test/proto_file_test.py @@ -0,0 +1,11 @@ +import unittest + +from src.proto_file import ProtoFile + + +class ProtoFileTest(unittest.TestCase): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/test/proto_float_test.py b/test/proto_float_test.py index fa3fde7..ecdbe22 100644 --- a/test/proto_float_test.py +++ b/test/proto_float_test.py @@ -10,10 +10,12 @@ def test_float(self): ProtoFloat(2834.235928, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match(".0").node, ProtoFloat(0.0, ProtoFloatSign.POSITIVE) + ProtoFloat.match(".0").node, + ProtoFloat(0.0, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match(".3265").node, ProtoFloat(0.3265, ProtoFloatSign.POSITIVE) + ProtoFloat.match(".3265").node, + ProtoFloat(0.3265, ProtoFloatSign.POSITIVE), ) def test_inf(self): @@ -34,13 +36,16 @@ def test_exponential_positive(self): ProtoFloat(283400, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match("2834e2").node, ProtoFloat(283400, ProtoFloatSign.POSITIVE) + ProtoFloat.match("2834e2").node, + ProtoFloat(283400, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match("2834.e0").node, ProtoFloat(2834, ProtoFloatSign.POSITIVE) + ProtoFloat.match("2834.e0").node, + ProtoFloat(2834, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match("2834e0").node, ProtoFloat(2834, ProtoFloatSign.POSITIVE) + ProtoFloat.match("2834e0").node, + ProtoFloat(2834, ProtoFloatSign.POSITIVE), ) self.assertEqual( ProtoFloat.match("2834.E3").node, @@ -59,13 +64,16 @@ def test_exponential_positive(self): ProtoFloat(283400, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match(".0E1").node, ProtoFloat(0, ProtoFloatSign.POSITIVE) + ProtoFloat.match(".0E1").node, + ProtoFloat(0, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match(".0E2").node, ProtoFloat(0, ProtoFloatSign.POSITIVE) + ProtoFloat.match(".0E2").node, + ProtoFloat(0, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match(".3265e1").node, ProtoFloat(3.265, ProtoFloatSign.POSITIVE) + ProtoFloat.match(".3265e1").node, + ProtoFloat(3.265, ProtoFloatSign.POSITIVE), ) def test_exponential_negative(self): @@ -74,10 +82,12 @@ def test_exponential_negative(self): ProtoFloat(28.34, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match("2834e-2").node, ProtoFloat(28.34, ProtoFloatSign.POSITIVE) + ProtoFloat.match("2834e-2").node, + ProtoFloat(28.34, ProtoFloatSign.POSITIVE), ) self.assertEqual( - ProtoFloat.match(".0e-1").node, ProtoFloat(0.00, ProtoFloatSign.POSITIVE) + ProtoFloat.match(".0e-1").node, + ProtoFloat(0.00, ProtoFloatSign.POSITIVE), ) diff --git a/test/proto_identifier_test.py b/test/proto_identifier_test.py index e174296..dd5435f 100644 --- a/test/proto_identifier_test.py +++ b/test/proto_identifier_test.py @@ -44,7 +44,8 @@ def test_full_ident(self): ProtoFullIdentifier.match("a.bar.b0").node.identifier, "a.bar.b0" ) self.assertEqual( - ProtoFullIdentifier.match("a.bar.b0.c_2a").node.identifier, "a.bar.b0.c_2a" + ProtoFullIdentifier.match("a.bar.b0.c_2a").node.identifier, + "a.bar.b0.c_2a", ) def test_full_ident_invalid_periods(self): diff --git a/test/proto_import_test.py b/test/proto_import_test.py index 0f74495..8806d8f 100644 --- a/test/proto_import_test.py +++ b/test/proto_import_test.py @@ -1,7 +1,14 @@ import unittest from textwrap import dedent -from src.proto_import import ProtoImport +from src.proto_import import ( + ProtoImport, + ProtoImportAdded, + ProtoImportMadeNonWeak, + ProtoImportMadePublic, + ProtoImportRemoved, +) +from src.proto_string_literal import ProtoStringLiteral class ImportTest(unittest.TestCase): @@ -60,7 +67,7 @@ def test_weak_mixed_imports(self): """import "foo.proto"; import weak "bar/baz.proto"; import "bat.proto";""" - ) + ), ) self.assertEqual(first_parsed_import.node.path.value, "foo.proto") self.assertEqual(first_parsed_import.node.weak, False) @@ -108,7 +115,7 @@ def test_public_mixed_imports(self): """import "foo.proto"; import public "bar/baz.proto"; import public "bat.proto";""" - ) + ), ) self.assertEqual(first_parsed_import.node.path.value, "foo.proto") self.assertEqual(first_parsed_import.node.public, False) @@ -128,6 +135,46 @@ def test_public_mixed_imports(self): third_parsed_import.node.serialize(), 'import public "bat.proto";' ) + def test_diff_sets_same_path_simple(self): + pf1 = ProtoImport(ProtoStringLiteral("path/to/some.proto")) + pf2 = ProtoImport(ProtoStringLiteral("path/to/some.proto")) + self.assertEqual(ProtoImport.diff_sets([pf1], [pf2]), []) + + def test_diff_sets_added_path_simple(self): + pf1 = ProtoImport(ProtoStringLiteral("path/to/some.proto")) + self.assertEqual(ProtoImport.diff_sets([pf1], []), [ProtoImportAdded(pf1)]) + + def test_diff_sets_removed_path_simple(self): + pf2 = ProtoImport(ProtoStringLiteral("path/to/some.proto")) + self.assertEqual(ProtoImport.diff_sets([], [pf2]), [ProtoImportRemoved(pf2)]) + + def test_diff_sets_different_path_simple(self): + pf1 = ProtoImport(ProtoStringLiteral("path/to/some.proto")) + pf2 = ProtoImport(ProtoStringLiteral("path/to/some/other.proto")) + self.assertEqual( + ProtoImport.diff_sets([pf1], [pf2]), + [ProtoImportAdded(pf1), ProtoImportRemoved(pf2)], + ) + + def test_diff_sets_changed_optional_attributes(self): + pf1 = ProtoImport( + ProtoStringLiteral("path/to/some.proto"), + weak=False, + public=True, + ) + pf2 = ProtoImport( + ProtoStringLiteral("path/to/some.proto"), + weak=True, + public=False, + ) + self.assertEqual( + ProtoImport.diff_sets([pf1], [pf2]), + [ + ProtoImportMadeNonWeak(pf2), + ProtoImportMadePublic(pf2), + ], + ) + if __name__ == "__main__": unittest.main() diff --git a/test/proto_map_test.py b/test/proto_map_test.py new file mode 100644 index 0000000..59e7ba1 --- /dev/null +++ b/test/proto_map_test.py @@ -0,0 +1,92 @@ +import unittest + +from src.proto_constant import ProtoConstant +from src.proto_identifier import ( + ProtoEnumOrMessageIdentifier, + ProtoFullIdentifier, + ProtoIdentifier, +) +from src.proto_int import ProtoInt, ProtoIntSign +from src.proto_map import ProtoMap, ProtoMapKeyTypesEnum, ProtoMapValueTypesEnum +from src.proto_message_field import ProtoMessageFieldOption +from src.proto_string_literal import ProtoStringLiteral + + +class MapTest(unittest.TestCase): + maxDiff = None + + def test_simple_map(self): + parsed_map_simple = ProtoMap.match("map my_map = 10;") + self.assertEqual( + parsed_map_simple.node, + ProtoMap( + ProtoMapKeyTypesEnum.SFIXED64, + ProtoMapValueTypesEnum.ENUM_OR_MESSAGE, + ProtoIdentifier("my_map"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ProtoEnumOrMessageIdentifier("NestedMessage"), + [], + ), + ) + + def test_map_without_spaces(self): + map_without_spaces = ProtoMap.match("map my_map = 10;") + self.assertEqual( + map_without_spaces.node, + ProtoMap( + ProtoMapKeyTypesEnum.SFIXED64, + ProtoMapValueTypesEnum.ENUM_OR_MESSAGE, + ProtoIdentifier("my_map"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ProtoEnumOrMessageIdentifier("NestedMessage"), + [], + ), + ) + + def test_map_with_options(self): + parsed_map_simple = ProtoMap.match( + "map my_map = 10 [ java_package = 'com.example.foo', baz.bat = 48 ];", + ) + self.assertEqual(parsed_map_simple.node.key_type, ProtoMapKeyTypesEnum.SFIXED64) + self.assertEqual( + parsed_map_simple.node.value_type, ProtoMapValueTypesEnum.ENUM_OR_MESSAGE + ) + self.assertEqual(parsed_map_simple.node.name, ProtoIdentifier("my_map")) + self.assertEqual( + parsed_map_simple.node.number, ProtoInt(10, ProtoIntSign.POSITIVE) + ) + self.assertEqual( + parsed_map_simple.node.enum_or_message_type_name, + ProtoEnumOrMessageIdentifier("NestedMessage"), + ) + self.assertEqual( + parsed_map_simple.node.options, + [ + ProtoMessageFieldOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("com.example.foo")), + ), + ProtoMessageFieldOption( + ProtoFullIdentifier("baz.bat"), + ProtoConstant(ProtoInt(48, ProtoIntSign.POSITIVE)), + ), + ], + ) + + def test_map_message_value(self): + parsed_map_simple = ProtoMap.match("map string_map = 11;") + self.assertEqual( + parsed_map_simple.node, + ProtoMap( + ProtoMapKeyTypesEnum.STRING, + ProtoMapValueTypesEnum.STRING, + ProtoIdentifier("string_map"), + ProtoInt(11, ProtoIntSign.POSITIVE), + None, + [], + ), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/proto_message_field_test.py b/test/proto_message_field_test.py index 8692322..fa6a882 100644 --- a/test/proto_message_field_test.py +++ b/test/proto_message_field_test.py @@ -6,7 +6,13 @@ ProtoIdentifier, ) from src.proto_int import ProtoInt, ProtoIntSign -from src.proto_message_field import ProtoMessageField, ProtoMessageFieldTypesEnum +from src.proto_message_field import ( + ProtoMessageField, + ProtoMessageFieldAdded, + ProtoMessageFieldNameChanged, + ProtoMessageFieldRemoved, + ProtoMessageFieldTypesEnum, +) class MessageFieldTest(unittest.TestCase): @@ -66,7 +72,7 @@ def test_message_field_starts_with_underscore(self): self.assertEqual( parsed_double_undescored_field.node, ProtoMessageField( - ProtoMessageFieldTypesEnum.STRING, + ProtoMessageFieldTypesEnum.BOOL, ProtoIdentifier("__test_field"), ProtoInt(1, ProtoIntSign.POSITIVE), ), @@ -117,3 +123,248 @@ def test_field_starts_with_period(self): ProtoEnumOrMessageIdentifier(".google.proto.FooType"), ), ) + + def test_diff_same_field_returns_empty(self): + pmf1 = ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("my_message_field"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ) + pmf2 = ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("my_message_field"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ) + self.assertEqual(ProtoMessageField.diff(None, pmf1, pmf2), []) + + def test_diff_different_field_name_same_number_returns_field_diff(self): + pmf1 = ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ) + pmf2 = ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ) + self.assertEqual( + [ + ProtoMessageFieldNameChanged( + None, + pmf1, + pmf2.name, + ) + ], + ProtoMessageField.diff(None, pmf1, pmf2), + ) + + def test_diff_field_removed(self): + pmf1 = ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(10, ProtoIntSign.POSITIVE), + ) + pmf2 = None + self.assertEqual( + [ + ProtoMessageFieldRemoved(None, pmf1), + ], + ProtoMessageField.diff(None, pmf1, pmf2), + ) + + def test_diff_sets_empty_returns_empty(self): + set1 = [] + set2 = [] + self.assertEqual(ProtoMessageField.diff_sets(None, set1, set2), []) + + def test_diff_sets_no_change(self): + set1 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz"), + ProtoInt(3, ProtoIntSign.POSITIVE), + ), + ] + self.assertEqual([], ProtoMessageField.diff_sets(None, set1, set1)) + + def test_diff_sets_all_removed(self): + set1 = [] + set2 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz"), + ProtoInt(3, ProtoIntSign.POSITIVE), + ), + ] + diff = ProtoMessageField.diff_sets(None, set1, set2) + + for pmf in set2: + self.assertIn( + ProtoMessageFieldRemoved(None, pmf), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_all_added(self): + set1 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz"), + ProtoInt(3, ProtoIntSign.POSITIVE), + ), + ] + set2 = [] + diff = ProtoMessageField.diff_sets(None, set1, set2) + + for pmf in set1: + self.assertIn( + ProtoMessageFieldAdded(None, pmf), + diff, + ) + + self.assertEqual(3, len(diff)) + + def test_diff_sets_mutually_exclusive(self): + set1 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz"), + ProtoInt(3, ProtoIntSign.POSITIVE), + ), + ] + set2 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo2"), + ProtoInt(4, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar2"), + ProtoInt(5, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz2"), + ProtoInt(6, ProtoIntSign.POSITIVE), + ), + ] + + diff = ProtoMessageField.diff_sets(None, set1, set2) + + for pmf in set1: + self.assertIn( + ProtoMessageFieldAdded(None, pmf), + diff, + ) + + for pmf in set2: + self.assertIn( + ProtoMessageFieldRemoved(None, pmf), + diff, + ) + + self.assertEqual(6, len(diff)) + + def test_diff_sets_overlap(self): + set1 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz"), + ProtoInt(3, ProtoIntSign.POSITIVE), + ), + ] + set2 = [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("foo2"), + ProtoInt(4, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("bar"), + ProtoInt(2, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.FLOAT, + ProtoIdentifier("baz2"), + ProtoInt(6, ProtoIntSign.POSITIVE), + ), + ] + + diff = ProtoMessageField.diff_sets(None, set1, set2) + + self.assertIn( + ProtoMessageFieldRemoved(None, set1[0]), + diff, + ) + + self.assertIn( + ProtoMessageFieldRemoved(None, set1[2]), + diff, + ) + self.assertIn( + ProtoMessageFieldAdded(None, set2[0]), + diff, + ) + self.assertIn( + ProtoMessageFieldAdded(None, set2[2]), + diff, + ) + + self.assertEqual(4, len(diff)) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/proto_message_test.py b/test/proto_message_test.py index 2dc0878..963adfc 100644 --- a/test/proto_message_test.py +++ b/test/proto_message_test.py @@ -13,18 +13,14 @@ ProtoIdentifier, ) from src.proto_int import ProtoInt, ProtoIntSign -from src.proto_message import ( - ProtoMap, - ProtoMapKeyTypesEnum, - ProtoMapValueTypesEnum, - ProtoMessage, - ProtoOneOf, -) +from src.proto_map import ProtoMap, ProtoMapKeyTypesEnum, ProtoMapValueTypesEnum +from src.proto_message import ProtoMessage, ProtoMessageAdded, ProtoMessageRemoved from src.proto_message_field import ( ProtoMessageField, ProtoMessageFieldOption, ProtoMessageFieldTypesEnum, ) +from src.proto_oneof import ProtoOneOf from src.proto_option import ProtoOption from src.proto_range import ProtoRange, ProtoRangeEnum from src.proto_reserved import ProtoReserved @@ -34,6 +30,8 @@ class MessageTest(unittest.TestCase): maxDiff = None + DEFAULT_PARENT = ProtoMessage(ProtoIdentifier("DefaultParent"), []) + def test_message_all_features(self): parsed_message_multiple_fields = ProtoMessage.match( dedent( @@ -61,7 +59,7 @@ def test_message_all_features(self): extensions 8 to max; } """.strip() - ) + ), ) self.assertEqual( parsed_message_multiple_fields.node.nodes, @@ -95,7 +93,7 @@ def test_message_all_features(self): ProtoInt(1, ProtoIntSign.POSITIVE), ProtoInt(3, ProtoIntSign.POSITIVE), ) - ] + ], ), ProtoSingleLineComment(" single-line comment"), ProtoMessageField( @@ -225,7 +223,7 @@ def test_empty_message(self): } """.strip() - ) + ), ) self.assertIsNotNone(parsed_spaced_message) self.assertEqual(parsed_spaced_message.node.name, ProtoIdentifier("FooMessage")) @@ -239,7 +237,7 @@ def test_message_empty_statements(self): ; } """.strip() - ) + ), ) self.assertIsNotNone(empty_statement_message) self.assertEqual( @@ -255,7 +253,7 @@ def test_message_optionals(self): option (foo.bar).baz = false; } """.strip() - ) + ), ) self.assertIsNotNone( parsed_message_with_optionals.node.options, @@ -286,7 +284,7 @@ def test_message_nested_enum(self): } } """.strip() - ) + ), ) self.assertEqual( parsed_message_with_enum.node, @@ -322,7 +320,7 @@ def test_message_nested_message(self): message NestedMessage {} } """.strip() - ) + ), ) self.assertEqual( parsed_message_with_enum.node, @@ -343,7 +341,7 @@ def test_message_reserved_single_field(self): reserved "foo", "barBaz"; } """.strip() - ) + ), ) self.assertEqual( parsed_message_with_reserved_single_field.node, @@ -361,13 +359,13 @@ def test_message_reserved_single_field(self): ProtoInt(72, ProtoIntSign.POSITIVE), ProtoRangeEnum.MAX, ), - ] + ], ), ProtoReserved( fields=[ ProtoIdentifier("foo"), ProtoIdentifier("barBaz"), - ] + ], ), ], ), @@ -381,7 +379,7 @@ def test_message_simple_field(self): string single_field = 1; } """.strip() - ) + ), ) self.assertEqual( parsed_message_with_single_field_simple.node, @@ -397,257 +395,6 @@ def test_message_simple_field(self): ), ) - def test_oneof_empty(self): - parsed_oneof_empty = ProtoOneOf.match(dedent("oneof one_of_field {}".strip())) - self.assertEqual( - parsed_oneof_empty.node, - ProtoOneOf( - ProtoIdentifier("one_of_field"), - [], - ), - ) - - def test_oneof_empty_statements(self): - parsed_oneof_empty = ProtoOneOf.match( - dedent( - """oneof one_of_field { - ; - ; - }""".strip() - ) - ) - self.assertEqual( - parsed_oneof_empty.node, - ProtoOneOf( - ProtoIdentifier("one_of_field"), - [], - ), - ) - - def test_oneof_basic_fields(self): - parsed_oneof_basic_fields = ProtoOneOf.match( - dedent( - """oneof one_of_field { - string name = 4; - SubMessage sub_message = 9; - }""".strip() - ) - ) - self.assertEqual( - parsed_oneof_basic_fields.node, - ProtoOneOf( - ProtoIdentifier("one_of_field"), - [ - ProtoMessageField( - ProtoMessageFieldTypesEnum.STRING, - ProtoIdentifier("name"), - ProtoInt(4, ProtoIntSign.POSITIVE), - ), - ProtoMessageField( - ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE, - ProtoIdentifier("sub_message"), - ProtoInt(9, ProtoIntSign.POSITIVE), - False, - False, - ProtoFullIdentifier("SubMessage"), - [], - ), - ], - ), - ) - - def test_oneof_options(self): - parsed_oneof_options = ProtoOneOf.match( - dedent( - """oneof one_of_field { - option java_package = "com.example.foo"; - }""".strip() - ) - ) - self.assertEqual( - parsed_oneof_options.node, - ProtoOneOf( - ProtoIdentifier("one_of_field"), - [ - ProtoOption( - ProtoIdentifier("java_package"), - ProtoConstant(ProtoStringLiteral("com.example.foo")), - ), - ], - ), - ) - - def test_oneof_field_option(self): - parsed_oneof_field_option = ProtoOneOf.match( - dedent( - """oneof one_of_field { - string name = 4 [ (bar.baz).bat = "bat", baz.bat = -100 ]; - }""".strip() - ) - ) - self.assertEqual( - parsed_oneof_field_option.node, - ProtoOneOf( - ProtoIdentifier("one_of_field"), - [ - ProtoMessageField( - ProtoMessageFieldTypesEnum.STRING, - ProtoIdentifier("name"), - ProtoInt(4, ProtoIntSign.POSITIVE), - False, - False, - None, - [ - ProtoMessageFieldOption( - ProtoIdentifier("(bar.baz).bat"), - ProtoConstant(ProtoStringLiteral("bat")), - ), - ProtoMessageFieldOption( - ProtoIdentifier("baz.bat"), - ProtoConstant(ProtoInt(100, ProtoIntSign.NEGATIVE)), - ), - ], - ) - ], - ), - ) - - def test_oneof_with_comment(self): - parsed_oneof_with_comment = ProtoOneOf.match( - dedent( - """oneof one_of_field { - string name = 4; - // single-line comment! - SubMessage sub_message = 9; - }""".strip() - ) - ) - self.assertEqual( - parsed_oneof_with_comment.node, - ProtoOneOf( - ProtoIdentifier("one_of_field"), - [ - ProtoMessageField( - ProtoMessageFieldTypesEnum.STRING, - ProtoIdentifier("name"), - ProtoInt(4, ProtoIntSign.POSITIVE), - ), - ProtoSingleLineComment(" single-line comment!"), - ProtoMessageField( - ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE, - ProtoIdentifier("sub_message"), - ProtoInt(9, ProtoIntSign.POSITIVE), - False, - False, - ProtoFullIdentifier("SubMessage"), - [], - ), - ], - ), - ) - - def test_oneof_normalize_removes_comment(self): - normalized_oneof = ProtoOneOf.match( - dedent( - """oneof one_of_field { - string name = 4; - // single-line comment! - SubMessage sub_message = 9; - }""".strip() - ) - ).node.normalize() - self.assertEqual( - normalized_oneof.nodes, - [ - ProtoMessageField( - ProtoMessageFieldTypesEnum.STRING, - ProtoIdentifier("name"), - ProtoInt(4, ProtoIntSign.POSITIVE), - ), - ProtoMessageField( - ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE, - ProtoIdentifier("sub_message"), - ProtoInt(9, ProtoIntSign.POSITIVE), - False, - False, - ProtoFullIdentifier("SubMessage"), - [], - ), - ], - ) - - def test_simple_map(self): - parsed_map_simple = ProtoMap.match("map my_map = 10;") - self.assertEqual( - parsed_map_simple.node, - ProtoMap( - ProtoMapKeyTypesEnum.SFIXED64, - ProtoMapValueTypesEnum.ENUM_OR_MESSAGE, - ProtoIdentifier("my_map"), - ProtoInt(10, ProtoIntSign.POSITIVE), - ProtoEnumOrMessageIdentifier("NestedMessage"), - [], - ), - ) - - def test_map_without_spaces(self): - map_without_spaces = ProtoMap.match("map my_map = 10;") - self.assertEqual( - map_without_spaces.node, - ProtoMap( - ProtoMapKeyTypesEnum.SFIXED64, - ProtoMapValueTypesEnum.ENUM_OR_MESSAGE, - ProtoIdentifier("my_map"), - ProtoInt(10, ProtoIntSign.POSITIVE), - ProtoEnumOrMessageIdentifier("NestedMessage"), - [], - ), - ) - - def test_map_with_options(self): - parsed_map_simple = ProtoMap.match( - "map my_map = 10 [ java_package = 'com.example.foo', baz.bat = 48 ];" - ) - self.assertEqual(parsed_map_simple.node.key_type, ProtoMapKeyTypesEnum.SFIXED64) - self.assertEqual( - parsed_map_simple.node.value_type, ProtoMapValueTypesEnum.ENUM_OR_MESSAGE - ) - self.assertEqual(parsed_map_simple.node.name, ProtoIdentifier("my_map")) - self.assertEqual( - parsed_map_simple.node.number, ProtoInt(10, ProtoIntSign.POSITIVE) - ) - self.assertEqual( - parsed_map_simple.node.enum_or_message_type_name, - ProtoEnumOrMessageIdentifier("NestedMessage"), - ) - self.assertEqual( - parsed_map_simple.node.options, - [ - ProtoMessageFieldOption( - ProtoIdentifier("java_package"), - ProtoConstant(ProtoStringLiteral("com.example.foo")), - ), - ProtoMessageFieldOption( - ProtoFullIdentifier("baz.bat"), - ProtoConstant(ProtoInt(48, ProtoIntSign.POSITIVE)), - ), - ], - ) - - def test_map_message_value(self): - parsed_map_simple = ProtoMap.match("map string_map = 11;") - self.assertEqual( - parsed_map_simple.node, - ProtoMap( - ProtoMapKeyTypesEnum.STRING, - ProtoMapValueTypesEnum.STRING, - ProtoIdentifier("string_map"), - ProtoInt(11, ProtoIntSign.POSITIVE), - None, - [], - ), - ) - def test_message_parses_comments(self): parsed_comments = ProtoMessage.match( dedent( @@ -664,7 +411,7 @@ def test_message_parses_comments(self): string baz = 3; } """.strip() - ) + ), ) self.assertEqual( parsed_comments.node.nodes, @@ -681,7 +428,7 @@ def test_message_parses_comments(self): ProtoInt(2, ProtoIntSign.POSITIVE), ), ProtoMultiLineComment( - "\n multiple\n line\n comment!\n " + "\n multiple\n line\n comment!\n ", ), ProtoMessageField( ProtoMessageFieldTypesEnum.STRING, @@ -702,7 +449,7 @@ def test_message_extends(self): } } """.strip() - ) + ), ) self.assertEqual( parsed_extends.node.nodes, @@ -716,7 +463,9 @@ def test_message_extends(self): ProtoEnumOrMessageIdentifier("SomeOtherMessage"), [ ProtoMessageField( - ProtoMessageFieldTypesEnum.STRING, ProtoIdentifier("foo"), 2 + ProtoMessageFieldTypesEnum.STRING, + ProtoIdentifier("foo"), + ProtoInt(2, ProtoIntSign.POSITIVE), ) ], ), @@ -739,7 +488,7 @@ def test_message_normalizes_away_comments(self): string baz = 3; } """.strip() - ) + ), ).node.normalize() self.assertEqual( parsed_comments.nodes, @@ -762,6 +511,211 @@ def test_message_normalizes_away_comments(self): ], ) + def test_diff_same_message_returns_empty(self): + pm1 = ProtoMessage( + ProtoIdentifier("MyMessage"), + [], + ) + pm2 = ProtoMessage( + ProtoIdentifier("MyMessage"), + [], + ) + self.assertEqual(ProtoMessage.diff(self.DEFAULT_PARENT, pm1, pm2), []) + + def test_diff_different_message_name_returns_empty(self): + pm1 = ProtoMessage( + ProtoIdentifier("MyMessage"), + [], + ) + pm2 = ProtoMessage( + ProtoIdentifier("OtherMessage"), + [], + ) + self.assertEqual(ProtoMessage.diff(self.DEFAULT_PARENT, pm1, pm2), []) + + def test_diff_message_added(self): + pm1 = None + pm2 = ProtoMessage(ProtoIdentifier("MyMessage"), []) + self.assertEqual( + ProtoMessage.diff(self.DEFAULT_PARENT, pm1, pm2), + [ + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("MyMessage"), []) + ), + ], + ) + + def test_diff_message_removed(self): + pm1 = ProtoMessage(ProtoIdentifier("MyMessage"), []) + pm2 = None + self.assertEqual( + ProtoMessage.diff(self.DEFAULT_PARENT, pm1, pm2), + [ + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("MyMessage"), []) + ), + ], + ) + + def test_diff_sets_empty_returns_empty(self): + set1 = [] + set2 = [] + self.assertEqual(ProtoMessage.diff_sets(self.DEFAULT_PARENT, set1, set2), []) + + def test_diff_sets_no_change_returns_empty(self): + set1 = [ + ProtoMessage(ProtoIdentifier("FooMessage"), []), + ProtoMessage(ProtoIdentifier("BarMessage"), []), + ProtoMessage(ProtoIdentifier("BazMessage"), []), + ] + self.assertEqual(ProtoMessage.diff_sets(self.DEFAULT_PARENT, set1, set1), []) + + def test_diff_sets_all_removed(self): + set1 = [ + ProtoMessage(ProtoIdentifier("FooMessage"), []), + ProtoMessage(ProtoIdentifier("BarMessage"), []), + ProtoMessage(ProtoIdentifier("BazMessage"), []), + ] + set2 = [] + diff = ProtoMessage.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("FooMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BarMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BazMessage"), []) + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_all_added(self): + set1 = [] + set2 = [ + ProtoMessage(ProtoIdentifier("FooMessage"), []), + ProtoMessage(ProtoIdentifier("BarMessage"), []), + ProtoMessage(ProtoIdentifier("BazMessage"), []), + ] + + diff = ProtoMessage.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("FooMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BarMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BazMessage"), []) + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_mutually_exclusive(self): + set1 = [ + ProtoMessage(ProtoIdentifier("FooMessage"), []), + ProtoMessage(ProtoIdentifier("BarMessage"), []), + ProtoMessage(ProtoIdentifier("BazMessage"), []), + ] + set2 = [ + ProtoMessage(ProtoIdentifier("FooMessage2"), []), + ProtoMessage(ProtoIdentifier("BarMessage2"), []), + ProtoMessage(ProtoIdentifier("BazMessage2"), []), + ] + diff = ProtoMessage.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("FooMessage2"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BarMessage2"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BazMessage2"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("FooMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BarMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BazMessage"), []) + ), + diff, + ) + self.assertEqual(6, len(diff)) + + def test_diff_sets_overlap(self): + + set1 = [ + ProtoMessage(ProtoIdentifier("FooMessage"), []), + ProtoMessage(ProtoIdentifier("BarMessage"), []), + ProtoMessage(ProtoIdentifier("BazMessage"), []), + ] + set2 = [ + ProtoMessage(ProtoIdentifier("FooMessage2"), []), + ProtoMessage(ProtoIdentifier("BarMessage"), []), + ProtoMessage(ProtoIdentifier("BazMessage2"), []), + ] + diff = ProtoMessage.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("FooMessage2"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageAdded( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BazMessage2"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("FooMessage"), []) + ), + diff, + ) + self.assertIn( + ProtoMessageRemoved( + self.DEFAULT_PARENT, ProtoMessage(ProtoIdentifier("BazMessage"), []) + ), + diff, + ) + self.assertEqual(4, len(diff)) + if __name__ == "__main__": unittest.main() diff --git a/test/proto_oneof_test.py b/test/proto_oneof_test.py new file mode 100644 index 0000000..fbcb350 --- /dev/null +++ b/test/proto_oneof_test.py @@ -0,0 +1,459 @@ +import unittest +from textwrap import dedent + +from src.proto_comment import ProtoSingleLineComment +from src.proto_constant import ProtoConstant +from src.proto_identifier import ProtoFullIdentifier, ProtoIdentifier +from src.proto_int import ProtoInt, ProtoIntSign +from src.proto_message_field import ( + ProtoMessageField, + ProtoMessageFieldAdded, + ProtoMessageFieldNameChanged, + ProtoMessageFieldOption, + ProtoMessageFieldRemoved, + ProtoMessageFieldTypesEnum, +) +from src.proto_oneof import ProtoOneOf, ProtoOneOfAdded, ProtoOneOfRemoved +from src.proto_option import ProtoOption +from src.proto_string_literal import ProtoStringLiteral + + +class OneOfTest(unittest.TestCase): + maxDiff = None + + DEFAULT_PARENT = ProtoOneOf( + ProtoIdentifier("default_parent"), + [], + ) + + def test_oneof_empty(self): + parsed_oneof_empty = ProtoOneOf.match(dedent("oneof one_of_field {}".strip())) + self.assertEqual( + parsed_oneof_empty.node, + ProtoOneOf( + ProtoIdentifier("one_of_field"), + [], + ), + ) + + def test_oneof_empty_statements(self): + parsed_oneof_empty = ProtoOneOf.match( + dedent( + """oneof one_of_field { + ; + ; + }""".strip() + ), + ) + self.assertEqual( + parsed_oneof_empty.node, + ProtoOneOf( + ProtoIdentifier("one_of_field"), + [], + ), + ) + + def test_oneof_basic_fields(self): + parsed_oneof_basic_fields = ProtoOneOf.match( + dedent( + """oneof one_of_field { + string name = 4; + SubMessage sub_message = 9; + }""".strip() + ), + ) + self.assertEqual( + parsed_oneof_basic_fields.node, + ProtoOneOf( + ProtoIdentifier("one_of_field"), + [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.STRING, + ProtoIdentifier("name"), + ProtoInt(4, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE, + ProtoIdentifier("sub_message"), + ProtoInt(9, ProtoIntSign.POSITIVE), + False, + False, + ProtoFullIdentifier("SubMessage"), + [], + ), + ], + ), + ) + + def test_oneof_options(self): + parsed_oneof_options = ProtoOneOf.match( + dedent( + """oneof one_of_field { + option java_package = "com.example.foo"; + }""".strip() + ), + ) + self.assertEqual( + parsed_oneof_options.node, + ProtoOneOf( + ProtoIdentifier("one_of_field"), + [ + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("com.example.foo")), + ), + ], + ), + ) + + def test_oneof_field_option(self): + parsed_oneof_field_option = ProtoOneOf.match( + dedent( + """oneof one_of_field { + string name = 4 [ (bar.baz).bat = "bat", baz.bat = -100 ]; + }""".strip() + ), + ) + self.assertEqual( + parsed_oneof_field_option.node, + ProtoOneOf( + ProtoIdentifier("one_of_field"), + [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.STRING, + ProtoIdentifier("name"), + ProtoInt(4, ProtoIntSign.POSITIVE), + False, + False, + None, + [ + ProtoMessageFieldOption( + ProtoIdentifier("(bar.baz).bat"), + ProtoConstant(ProtoStringLiteral("bat")), + ), + ProtoMessageFieldOption( + ProtoIdentifier("baz.bat"), + ProtoConstant(ProtoInt(100, ProtoIntSign.NEGATIVE)), + ), + ], + ) + ], + ), + ) + + def test_oneof_with_comment(self): + parsed_oneof_with_comment = ProtoOneOf.match( + dedent( + """oneof one_of_field { + string name = 4; + // single-line comment! + SubMessage sub_message = 9; + }""".strip() + ), + ) + self.assertEqual( + parsed_oneof_with_comment.node, + ProtoOneOf( + ProtoIdentifier("one_of_field"), + [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.STRING, + ProtoIdentifier("name"), + ProtoInt(4, ProtoIntSign.POSITIVE), + ), + ProtoSingleLineComment(" single-line comment!"), + ProtoMessageField( + ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE, + ProtoIdentifier("sub_message"), + ProtoInt(9, ProtoIntSign.POSITIVE), + False, + False, + ProtoFullIdentifier("SubMessage"), + [], + ), + ], + ), + ) + + def test_oneof_normalize_removes_comment(self): + normalized_oneof = ProtoOneOf.match( + dedent( + """oneof one_of_field { + string name = 4; + // single-line comment! + SubMessage sub_message = 9; + }""".strip() + ), + ).node.normalize() + self.assertEqual( + normalized_oneof.nodes, + [ + ProtoMessageField( + ProtoMessageFieldTypesEnum.STRING, + ProtoIdentifier("name"), + ProtoInt(4, ProtoIntSign.POSITIVE), + ), + ProtoMessageField( + ProtoMessageFieldTypesEnum.ENUM_OR_MESSAGE, + ProtoIdentifier("sub_message"), + ProtoInt(9, ProtoIntSign.POSITIVE), + False, + False, + ProtoFullIdentifier("SubMessage"), + [], + ), + ], + ) + + def test_diff_same_oneof_returns_empty(self): + po1 = ProtoOneOf( + ProtoIdentifier("my_one_of"), + [], + ) + po2 = ProtoOneOf( + ProtoIdentifier("my_one_of"), + [], + ) + self.assertEqual(ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), []) + + def test_diff_different_oneof_name_returns_empty(self): + po1 = ProtoOneOf( + ProtoIdentifier("my_one_of"), + [], + ) + po2 = ProtoOneOf( + ProtoIdentifier("other_one_of"), + [], + ) + self.assertEqual(ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), []) + + def test_diff_oneof_added(self): + po1 = None + po2 = ProtoOneOf(ProtoIdentifier("my_one_of"), []) + self.assertEqual( + ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), + [ + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("my_one_of"), []) + ), + ], + ) + + def test_diff_oneof_removed(self): + po1 = ProtoOneOf(ProtoIdentifier("my_one_of"), []) + po2 = None + self.assertEqual( + [ + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("my_one_of"), []) + ), + ], + ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), + ) + + def test_diff_member_added(self): + po1 = ProtoOneOf(ProtoIdentifier("my_one_of"), []) + mf = ProtoMessageField( + ProtoMessageFieldTypesEnum.BOOL, + ProtoIdentifier("new_member"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ) + po2 = ProtoOneOf(ProtoIdentifier("my_one_of"), [mf]) + self.assertEqual( + [ProtoMessageFieldAdded(po1, mf)], + ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), + ) + + def test_diff_member_removed(self): + mf = ProtoMessageField( + ProtoMessageFieldTypesEnum.BOOL, + ProtoIdentifier("new_member"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ) + po1 = ProtoOneOf(ProtoIdentifier("my_one_of"), [mf]) + po2 = ProtoOneOf(ProtoIdentifier("my_one_of"), []) + self.assertEqual( + [ProtoMessageFieldRemoved(po1, mf)], + ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), + ) + + def test_diff_member_changed(self): + mf1 = ProtoMessageField( + ProtoMessageFieldTypesEnum.BOOL, + ProtoIdentifier("new_member"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ) + po1 = ProtoOneOf(ProtoIdentifier("my_one_of"), [mf1]) + mf2 = ProtoMessageField( + ProtoMessageFieldTypesEnum.BOOL, + ProtoIdentifier("new_member_changed"), + ProtoInt(1, ProtoIntSign.POSITIVE), + ) + po2 = ProtoOneOf(ProtoIdentifier("my_one_of"), [mf2]) + self.assertEqual( + [ProtoMessageFieldNameChanged(po1, mf1, mf2.name)], + ProtoOneOf.diff(self.DEFAULT_PARENT, po1, po2), + ) + + def test_diff_sets_empty_returns_empty(self): + set1 = [] + set2 = [] + self.assertEqual(ProtoOneOf.diff_sets(self.DEFAULT_PARENT, set1, set2), []) + + def test_diff_sets_no_change_returns_empty(self): + set1 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of"), []), + ] + self.assertEqual(ProtoOneOf.diff_sets(self.DEFAULT_PARENT, set1, set1), []) + + def test_diff_sets_all_removed(self): + set1 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of"), []), + ] + set2 = [] + diff = ProtoOneOf.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("foo_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("bar_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("baz_one_of"), []) + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_all_added(self): + set1 = [] + set2 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of"), []), + ] + + diff = ProtoOneOf.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("foo_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("bar_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("baz_one_of"), []) + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_mutually_exclusive(self): + set1 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of"), []), + ] + set2 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of2"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of2"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of2"), []), + ] + diff = ProtoOneOf.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("foo_one_of2"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("bar_one_of2"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("baz_one_of2"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("foo_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("bar_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("baz_one_of"), []) + ), + diff, + ) + self.assertEqual(6, len(diff)) + + def test_diff_sets_overlap(self): + + set1 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of"), []), + ] + set2 = [ + ProtoOneOf(ProtoIdentifier("foo_one_of2"), []), + ProtoOneOf(ProtoIdentifier("bar_one_of"), []), + ProtoOneOf(ProtoIdentifier("baz_one_of2"), []), + ] + diff = ProtoOneOf.diff_sets(self.DEFAULT_PARENT, set1, set2) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("foo_one_of2"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfAdded( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("baz_one_of2"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("foo_one_of"), []) + ), + diff, + ) + self.assertIn( + ProtoOneOfRemoved( + self.DEFAULT_PARENT, ProtoOneOf(ProtoIdentifier("baz_one_of"), []) + ), + diff, + ) + self.assertEqual(4, len(diff)) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/proto_option_test.py b/test/proto_option_test.py index 6d353fc..f7a6a00 100644 --- a/test/proto_option_test.py +++ b/test/proto_option_test.py @@ -5,11 +5,23 @@ from src.proto_float import ProtoFloat, ProtoFloatSign from src.proto_identifier import ProtoFullIdentifier, ProtoIdentifier from src.proto_int import ProtoInt, ProtoIntSign -from src.proto_option import ProtoOption +from src.proto_option import ( + ProtoOption, + ProtoOptionAdded, + ProtoOptionRemoved, + ProtoOptionValueChanged, +) from src.proto_string_literal import ProtoStringLiteral class OptionTest(unittest.TestCase): + + maxDiff = None + DEFAULT_PARENT = ProtoOption( + ProtoIdentifier("default.parent"), + ProtoConstant(ProtoInt(1, ProtoIntSign.POSITIVE)), + ) + def test_string_option(self): string_option = ProtoOption.match("option foo = 'test value';") self.assertEqual(string_option.node.name, ProtoIdentifier("foo")) @@ -36,14 +48,16 @@ def test_parenthesized_name(self): parenthesized_option = ProtoOption.match("option (foo) = 'test value';") self.assertEqual(parenthesized_option.node.name, ProtoIdentifier("(foo)")) self.assertEqual( - parenthesized_option.node.value.value, ProtoStringLiteral("test value") + parenthesized_option.node.value.value, + ProtoStringLiteral("test value"), ) fully_qualified_name_option = ProtoOption.match( "option (foo).bar.baz = 'test value';" ) self.assertEqual( - fully_qualified_name_option.node.name, ProtoFullIdentifier("(foo).bar.baz") + fully_qualified_name_option.node.name, + ProtoFullIdentifier("(foo).bar.baz"), ) self.assertEqual( fully_qualified_name_option.node.value.value, @@ -100,28 +114,32 @@ def test_int_option(self): int_option = ProtoOption.match("option foo = 0;") self.assertEqual(int_option.node.name, ProtoIdentifier("foo")) self.assertEqual( - int_option.node.value, ProtoConstant(ProtoInt(0, ProtoIntSign.POSITIVE)) + int_option.node.value, + ProtoConstant(ProtoInt(0, ProtoIntSign.POSITIVE)), ) self.assertEqual(int_option.remaining_source, "") int_option = ProtoOption.match("option foo = 12345;") self.assertEqual(int_option.node.name, ProtoIdentifier("foo")) self.assertEqual( - int_option.node.value, ProtoConstant(ProtoInt(12345, ProtoIntSign.POSITIVE)) + int_option.node.value, + ProtoConstant(ProtoInt(12345, ProtoIntSign.POSITIVE)), ) self.assertEqual(int_option.remaining_source, "") int_option = ProtoOption.match("option foo = +42;") self.assertEqual(int_option.node.name, ProtoIdentifier("foo")) self.assertEqual( - int_option.node.value, ProtoConstant(ProtoInt(42, ProtoIntSign.POSITIVE)) + int_option.node.value, + ProtoConstant(ProtoInt(42, ProtoIntSign.POSITIVE)), ) self.assertEqual(int_option.remaining_source, "") int_option = ProtoOption.match("option foo = -12;") self.assertEqual(int_option.node.name, ProtoIdentifier("foo")) self.assertEqual( - int_option.node.value, ProtoConstant(ProtoInt(12, ProtoIntSign.NEGATIVE)) + int_option.node.value, + ProtoConstant(ProtoInt(12, ProtoIntSign.NEGATIVE)), ) self.assertEqual(int_option.remaining_source, "") @@ -192,6 +210,392 @@ def test_float_option(self): ProtoConstant(ProtoFloat(float(120), ProtoFloatSign.NEGATIVE)), ) + def test_diff_same_option_returns_empty(self): + po1 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + po2 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + self.assertEqual(ProtoOption.diff(self.DEFAULT_PARENT, po1, po2), []) + + def test_diff_different_option_name_returns_empty(self): + po1 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + po2 = ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + self.assertEqual(ProtoOption.diff(self.DEFAULT_PARENT, po1, po2), []) + + def test_diff_different_option_value_returns_option_diff(self): + po1 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + po2 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("other value")), + ) + self.assertEqual( + ProtoOption.diff(self.DEFAULT_PARENT, po1, po2), + [ + ProtoOptionValueChanged( + self.DEFAULT_PARENT, + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ProtoConstant(ProtoStringLiteral("other value")), + ) + ], + ) + + def test_diff_option_added(self): + po1 = None + po2 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + self.assertEqual( + ProtoOption.diff(self.DEFAULT_PARENT, po1, po2), + [ + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + ], + ) + + def test_diff_option_removed(self): + po1 = ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ) + po2 = None + self.assertEqual( + ProtoOption.diff(self.DEFAULT_PARENT, po1, po2), + [ + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + ], + ) + + def test_diff_sets_empty_returns_empty(self): + set1 = [] + set2 = [] + self.assertEqual(ProtoOption.diff_sets(self.DEFAULT_PARENT, set1, set2), []) + + def test_diff_sets_no_change(self): + set1 = [ + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + self.assertEqual(ProtoOption.diff_sets(self.DEFAULT_PARENT, set1, set1), []) + + def test_diff_sets_all_removed(self): + set1 = [ + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + set2 = [] + diff = ProtoOption.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_all_added(self): + set1 = [] + set2 = [ + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + diff = ProtoOption.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ), + diff, + ) + self.assertEqual(3, len(diff)) + + def test_diff_sets_mutually_exclusive(self): + set1 = [ + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + set2 = [ + ProtoOption( + ProtoIdentifier("some.custom.option.but.not.prior"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("ruby_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ProtoOption( + ProtoIdentifier("other.option.but.stil.not.prior"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + + diff = ProtoOption.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option.but.not.prior"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("other.option.but.stil.not.prior"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ), + diff, + ) + + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("ruby_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ), + diff, + ) + + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ), + diff, + ) + + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + diff, + ) + + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ), + diff, + ) + + self.assertEqual(6, len(diff)) + + def test_diff_sets_overlap(self): + set1 = [ + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ), + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + set2 = [ + ProtoOption( + ProtoIdentifier("some.custom.option.but.not.prior"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ProtoOption( + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.bat")), + ), + ProtoOption( + ProtoIdentifier("other.option.but.stil.not.prior"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ] + + diff = ProtoOption.diff_sets(self.DEFAULT_PARENT, set1, set2) + + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + diff, + ) + + self.assertIn( + ProtoOptionRemoved( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("other.option"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("some.custom.option.but.not.prior"), + ProtoConstant(ProtoStringLiteral("some value")), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionAdded( + self.DEFAULT_PARENT, + ProtoOption( + ProtoIdentifier("other.option.but.stil.not.prior"), + ProtoConstant(ProtoInt(100, ProtoIntSign.POSITIVE)), + ), + ), + diff, + ) + self.assertIn( + ProtoOptionValueChanged( + self.DEFAULT_PARENT, + ProtoIdentifier("java_package"), + ProtoConstant(ProtoStringLiteral("foo.bar.baz")), + ProtoConstant(ProtoStringLiteral("foo.bar.bat")), + ), + diff, + ) + self.assertEqual(5, len(diff)) + if __name__ == "__main__": unittest.main() diff --git a/test/proto_package_test.py b/test/proto_package_test.py index 491708f..bf6bd7d 100644 --- a/test/proto_package_test.py +++ b/test/proto_package_test.py @@ -1,7 +1,12 @@ import unittest from textwrap import dedent -from src.proto_package import ProtoPackage +from src.proto_package import ( + ProtoPackage, + ProtoPackageAdded, + ProtoPackageChanged, + ProtoPackageRemoved, +) class PackageTest(unittest.TestCase): @@ -17,7 +22,8 @@ def test_package_serialize(self): ProtoPackage.match("package foo;").node.serialize(), "package foo;" ) self.assertEqual( - ProtoPackage.match("package foo.bar;").node.serialize(), "package foo.bar;" + ProtoPackage.match("package foo.bar;").node.serialize(), + "package foo.bar;", ) self.assertEqual( ProtoPackage.match("package foo.bar.baz;").node.serialize(), @@ -55,6 +61,44 @@ def test_package_malformed(self): with self.assertRaises(ValueError): ProtoPackage.match("packagefoo.bar;") + def test_diff_same_package_returns_empty(self): + pp1 = ProtoPackage("my.awesome.package") + pp2 = ProtoPackage("my.awesome.package") + self.assertEqual(ProtoPackage.diff(pp1, pp2), []) + + def test_diff_different_package_returns_package_diff(self): + pp1 = ProtoPackage("my.awesome.package") + pp2 = ProtoPackage("my.other.awesome.package") + self.assertEqual( + ProtoPackage.diff(pp1, pp2), + [ + ProtoPackageChanged( + ProtoPackage("my.awesome.package"), + ProtoPackage("my.other.awesome.package"), + ) + ], + ) + + def test_diff_package_added(self): + pp1 = None + pp2 = ProtoPackage("my.new.package") + self.assertEqual( + [ + ProtoPackageAdded(ProtoPackage("my.new.package")), + ], + ProtoPackage.diff(pp1, pp2), + ) + + def test_diff_package_removed(self): + pp1 = ProtoPackage("my.old.package") + pp2 = None + self.assertEqual( + [ + ProtoPackageRemoved(ProtoPackage("my.old.package")), + ], + ProtoPackage.diff(pp1, pp2), + ) + if __name__ == "__main__": unittest.main() diff --git a/test/proto_reserved_test.py b/test/proto_reserved_test.py index 0232905..e83aca8 100644 --- a/test/proto_reserved_test.py +++ b/test/proto_reserved_test.py @@ -27,10 +27,12 @@ def test_reserved_range_max(self): def test_reserved_single_string_field(self): self.assertEqual( - ProtoReserved.match("reserved 'foo';").node.serialize(), "reserved 'foo';" + ProtoReserved.match("reserved 'foo';").node.serialize(), + "reserved 'foo';", ) self.assertEqual( - ProtoReserved.match('reserved "foo";').node.serialize(), 'reserved "foo";' + ProtoReserved.match('reserved "foo";').node.serialize(), + 'reserved "foo";', ) def test_reserved_multiple_string_fields(self): diff --git a/test/proto_service_test.py b/test/proto_service_test.py index f294e02..0b5a4fb 100644 --- a/test/proto_service_test.py +++ b/test/proto_service_test.py @@ -35,7 +35,7 @@ def test_service_all_features(self): rpc ThreeRPC (ThreeRPCRequest) returns (ThreeRPCResponse) { option java_package = "com.example.foo"; option (foo.bar).baz = false; } } """.strip() - ) + ), ) self.assertEqual( test_service_all_features.node, @@ -60,7 +60,7 @@ def test_service_all_features(self): True, ), ProtoMultiLineComment( - "\n multiple\n line\n comment\n " + "\n multiple\n line\n comment\n ", ), ProtoServiceRPC( ProtoIdentifier("ThreeRPC"), @@ -114,11 +114,12 @@ def test_service_empty(self): } """.strip() - ) + ), ) self.assertIsNotNone(parsed_spaced_service) self.assertEqual( - parsed_spaced_service.node, ProtoService(ProtoIdentifier("FooService"), []) + parsed_spaced_service.node, + ProtoService(ProtoIdentifier("FooService"), []), ) def test_service_empty_statements(self): @@ -130,7 +131,7 @@ def test_service_empty_statements(self): ; } """.strip() - ) + ), ) self.assertIsNotNone(empty_statement_service) self.assertEqual( @@ -146,7 +147,7 @@ def test_service_option(self): option (foo.bar).baz = "bat"; } """.strip() - ) + ), ) self.assertEqual( service_with_options.node.nodes, @@ -168,7 +169,7 @@ def test_service_rpc_basic(self): rpc ThreeRPC (ThreeRPCRequest) returns (ThreeRPCResponse); } """.strip() - ) + ), ) self.assertEqual( service_with_options.node.nodes, @@ -200,7 +201,7 @@ def test_service_rpc_stream(self): rpc TwoRPC (TwoRPCRequest) returns (stream TwoRPCResponse); } """.strip() - ) + ), ) self.assertEqual( service_with_options.node.nodes, @@ -231,7 +232,7 @@ def test_service_rpc_options(self): rpc TwoRPC (TwoRPCRequest) returns (TwoRPCResponse) { option java_package = "com.example.foo"; option (foo.bar).baz = false; } } """.strip() - ) + ), ) self.assertEqual( service_with_options.node.nodes, @@ -277,7 +278,7 @@ def test_service_parses_comments(self): rpc ThreeRPC (ThreeRPCRequest) returns (ThreeRPCResponse); } """.strip() - ) + ), ) self.assertEqual( service_with_comments.node.nodes, @@ -294,7 +295,7 @@ def test_service_parses_comments(self): ProtoEnumOrMessageIdentifier("TwoRPCResponse"), ), ProtoMultiLineComment( - "\n multiple\n line\n comment\n " + "\n multiple\n line\n comment\n ", ), ProtoServiceRPC( ProtoIdentifier("ThreeRPC"), @@ -320,7 +321,7 @@ def test_service_normalize_removes_comments(self): rpc ThreeRPC (ThreeRPCRequest) returns (ThreeRPCResponse); } """.strip() - ) + ), ).node.normalize() self.assertEqual( normalized_service.nodes, diff --git a/test/proto_syntax_test.py b/test/proto_syntax_test.py index 1d2aae7..6abfda1 100644 --- a/test/proto_syntax_test.py +++ b/test/proto_syntax_test.py @@ -1,7 +1,7 @@ import unittest from src.proto_string_literal import ProtoStringLiteral -from src.proto_syntax import ProtoSyntax, ProtoSyntaxType +from src.proto_syntax import ProtoSyntax, ProtoSyntaxChanged class SyntaxTest(unittest.TestCase): @@ -75,6 +75,24 @@ def test_syntax_malformed(self): with self.assertRaises(ValueError): ProtoSyntax.match("syntax = proton") + def test_diff_empty_same_syntax_returns_empty(self): + pf1 = ProtoSyntax(ProtoStringLiteral("proto3")) + pf2 = ProtoSyntax(ProtoStringLiteral("proto3")) + self.assertEqual(ProtoSyntax.diff(pf1, pf2), []) + + def test_diff_empty_different_syntax_returns_syntax_diff(self): + pf1 = ProtoSyntax(ProtoStringLiteral("proto3")) + pf2 = ProtoSyntax(ProtoStringLiteral("proto2")) + self.assertEqual( + ProtoSyntax.diff(pf1, pf2), + [ + ProtoSyntaxChanged( + ProtoSyntax(ProtoStringLiteral("proto3")), + ProtoSyntax(ProtoStringLiteral("proto2")), + ) + ], + ) + if __name__ == "__main__": unittest.main() diff --git a/test/resources/single_message.proto b/test/resources/single_message.proto new file mode 100644 index 0000000..4b3d2f1 --- /dev/null +++ b/test/resources/single_message.proto @@ -0,0 +1,3 @@ +syntax = "proto3"; + +message MyMessage {} diff --git a/test/util/BUILD.bazel b/test/util/BUILD.bazel new file mode 100644 index 0000000..5e57fb2 --- /dev/null +++ b/test/util/BUILD.bazel @@ -0,0 +1,44 @@ +load("@rules_python//python:defs.bzl", "py_test") + +py_test( + name = "parser_test", + srcs = ["parser_test.py"], + deps = [ + "//src:proto_comment", + "//src:proto_constant", + "//src:proto_enum", + "//src:proto_extend", + "//src:proto_extensions", + "//src:proto_float", + "//src:proto_identifier", + "//src:proto_import", + "//src:proto_int", + "//src:proto_map", + "//src:proto_message", + "//src:proto_message_field", + "//src:proto_option", + "//src:proto_service", + "//src:proto_string_literal", + "//src:proto_syntax", + "//src/util:parser", + ], +) + +sh_test( + name = "parser_binary_test", + srcs = ["parser_binary_test.sh"], + data = [ + "//src/util:parser_binary", + "//test/resources:all_protos", + "@com_google_protobuf//:all_proto", + ], +) + +sh_test( + name = "compatibility_checker_binary_test", + srcs = ["compatibility_checker_binary_test.sh"], + data = [ + "//src/util:compatibility_checker_binary", + "//test/resources:all_protos", + ], +) diff --git a/test/util/compatibility_checker_binary_test.sh b/test/util/compatibility_checker_binary_test.sh new file mode 100755 index 0000000..63cd61f --- /dev/null +++ b/test/util/compatibility_checker_binary_test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euxo pipefail + +./src/util/compatibility_checker_binary ./test/resources/empty.proto ./test/resources/single_message.proto diff --git a/test/parser_binary_test.sh b/test/util/parser_binary_test.sh similarity index 91% rename from test/parser_binary_test.sh rename to test/util/parser_binary_test.sh index e8bc822..f6b6d35 100755 --- a/test/parser_binary_test.sh +++ b/test/util/parser_binary_test.sh @@ -4,7 +4,7 @@ set -euo pipefail function compare_proto() { # Takes one argument, the relative path to the proto to compare. echo $1 - ./src/parser_binary $1 > "${1}_serialized.proto" + ./src/util/parser_binary $1 > "${1}_serialized.proto" diff --side-by-side --ignore-all-space --ignore-blank-lines $1 "${1}_serialized.proto" } diff --git a/test/parser_test.py b/test/util/parser_test.py similarity index 97% rename from test/parser_test.py rename to test/util/parser_test.py index 7f42e1a..a610651 100644 --- a/test/parser_test.py +++ b/test/util/parser_test.py @@ -1,7 +1,6 @@ import unittest from textwrap import dedent -from src.parser import ParseError, Parser from src.proto_bool import ProtoBool from src.proto_comment import ProtoSingleLineComment from src.proto_constant import ProtoConstant @@ -16,15 +15,12 @@ ) from src.proto_import import ProtoImport from src.proto_int import ProtoInt, ProtoIntSign -from src.proto_message import ( - ProtoMap, - ProtoMapKeyTypesEnum, - ProtoMapValueTypesEnum, - ProtoMessage, +from src.proto_map import ProtoMap, ProtoMapKeyTypesEnum, ProtoMapValueTypesEnum +from src.proto_message import ProtoMessage, ProtoOneOf +from src.proto_message_field import ( ProtoMessageField, ProtoMessageFieldOption, ProtoMessageFieldTypesEnum, - ProtoOneOf, ) from src.proto_option import ProtoOption from src.proto_range import ProtoRange, ProtoRangeEnum @@ -32,6 +28,7 @@ from src.proto_service import ProtoService, ProtoServiceRPC from src.proto_string_literal import ProtoStringLiteral from src.proto_syntax import ProtoSyntaxType +from src.util.parser import ParseError, Parser class IntTest(unittest.TestCase): @@ -126,7 +123,8 @@ def test_parser(self): ProtoIdentifier("MyAwesomeEnum"), [ ProtoOption( - ProtoIdentifier("allow_alias"), ProtoConstant(ProtoBool(True)) + ProtoIdentifier("allow_alias"), + ProtoConstant(ProtoBool(True)), ), ProtoEnumValue( ProtoIdentifier("MAE_UNSPECIFIED"), @@ -152,7 +150,7 @@ def test_parser(self): ProtoIdentifier("MyAwesomeMessage"), [ ProtoOption( - ProtoIdentifier("(bar).baz"), + ProtoFullIdentifier("(bar).baz"), ProtoConstant(ProtoFloat(1.2, ProtoFloatSign.POSITIVE)), ), ProtoEnum( @@ -179,7 +177,7 @@ def test_parser(self): ProtoInt(1, ProtoIntSign.POSITIVE), ProtoInt(3, ProtoIntSign.POSITIVE), ) - ] + ], ), ProtoReserved(fields=[ProtoIdentifier("yay")]), ProtoSingleLineComment(" testing nested comment"), @@ -231,7 +229,7 @@ def test_parser(self): ProtoMessageFieldOption( ProtoIdentifier("baz.bat"), ProtoConstant( - ProtoInt(100, ProtoIntSign.NEGATIVE) + ProtoInt(100, ProtoIntSign.NEGATIVE), ), ), ], @@ -300,7 +298,7 @@ def test_parser(self): ProtoMessageField( ProtoMessageFieldTypesEnum.STRING, ProtoIdentifier("some_extendable_field"), - 1, + ProtoInt(1, ProtoIntSign.POSITIVE), ), ProtoSingleLineComment(" yay"), ],