From 7827f3ab44ac8c0618e8e42007004a925b801c83 Mon Sep 17 00:00:00 2001 From: Rasmus Ros Date: Thu, 29 Oct 2026 13:31:06 +0100 Subject: [PATCH] ci: standardize small-library builds --- .gitattributes | 3 + .github/actions/setup-build/action.yml | 43 ++++ .github/scripts/release-version.py | 49 ++++ .github/scripts/test-release-version.py | 45 ++++ .github/scripts/test-verification.py | 41 +++ .github/scripts/verify-consumer.sh | 10 + .github/scripts/verify-publication.py | 80 ++++++ .github/scripts/verify-test-results.py | 33 +++ .github/workflows/build.yml | 134 ++++++++-- .github/workflows/release.yml | 121 ++++----- .gitignore | 3 + README.md | 47 ++++ api-full/kencode.api | 323 ++++++++++++++++++++++++ api-full/kencode.klib.api | 316 +++++++++++++++++++++++ api/kencode.klib.api | 2 +- build.gradle.kts | 39 ++- consumer/build.gradle.kts | 21 ++ consumer/gradle.properties | 4 + consumer/settings.gradle.kts | 29 +++ consumer/src/main/java/Consumer.java | 24 ++ gradle.properties | 1 + settings.gradle.kts | 2 +- 22 files changed, 1264 insertions(+), 106 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/actions/setup-build/action.yml create mode 100644 .github/scripts/release-version.py create mode 100644 .github/scripts/test-release-version.py create mode 100644 .github/scripts/test-verification.py create mode 100644 .github/scripts/verify-consumer.sh create mode 100644 .github/scripts/verify-publication.py create mode 100644 .github/scripts/verify-test-results.py create mode 100644 api-full/kencode.api create mode 100644 api-full/kencode.klib.api create mode 100644 consumer/build.gradle.kts create mode 100644 consumer/gradle.properties create mode 100644 consumer/settings.gradle.kts create mode 100644 consumer/src/main/java/Consumer.java diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..74bcfcc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# ABI references must retain the Kotlin generator's exact trailing blank lines. +api/*.api whitespace=-blank-at-eof +api-full/*.api whitespace=-blank-at-eof diff --git a/.github/actions/setup-build/action.yml b/.github/actions/setup-build/action.yml new file mode 100644 index 0000000..e27d53a --- /dev/null +++ b/.github/actions/setup-build/action.yml @@ -0,0 +1,43 @@ +name: Set up library build +description: Install the JVM runtimes and restore Gradle and Kotlin Native caches. +inputs: + cache-encryption-key: + description: Encryption key for the Gradle configuration cache + required: false + gradle-cache-read-only: + description: Restore Gradle User Home without saving publication-job state + default: 'false' + target-profile: + description: Separate cached Native and configuration state for normal and full targets + default: default +runs: + using: composite + steps: + - uses: actions/setup-java@v6 + with: + distribution: temurin + java-version: | + 17 + 25 + - uses: gradle/actions/setup-gradle@v6 + with: + cache-read-only: ${{ inputs.gradle-cache-read-only }} + cache-encryption-key: ${{ inputs.cache-encryption-key }} + - uses: actions/cache@v6 + with: + path: ~/.konan + key: ${{ runner.os }}-${{ runner.arch }}-konan-${{ inputs.target-profile }}-${{ hashFiles('build.gradle.kts', 'settings.gradle.kts', 'gradle.properties', 'gradle/wrapper/gradle-wrapper.properties', 'gradle/*.versions.toml') }} + restore-keys: | + ${{ runner.os }}-${{ runner.arch }}-konan-${{ inputs.target-profile }}- + ${{ runner.os }}-${{ runner.arch }}-konan- + # setup-gradle v6 caches Gradle User Home; persist project entries explicitly. + # GRADLE_ENCRYPTION_KEY is exported by setup-gradle and Gradle encrypts these files. + - name: Cache encrypted configuration state + if: ${{ inputs.cache-encryption-key != '' }} + uses: actions/cache@v6 + with: + path: | + .gradle/configuration-cache + consumer/.gradle/configuration-cache + key: ${{ runner.os }}-${{ runner.arch }}-configuration-${{ inputs.target-profile }}-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', 'gradle/wrapper/gradle-wrapper.properties') }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-${{ runner.arch }}-configuration-${{ inputs.target-profile }}-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', 'gradle/wrapper/gradle-wrapper.properties') }}- diff --git a/.github/scripts/release-version.py b/.github/scripts/release-version.py new file mode 100644 index 0000000..2667ec5 --- /dev/null +++ b/.github/scripts/release-version.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Resolve release, snapshot and GitHub-only release-candidate versions.""" + +import os +import re +import subprocess +from pathlib import Path + +CORE = r"(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)" +STABLE = re.compile(CORE) +RC = re.compile(rf"({CORE})-rc\.(0|[1-9][0-9]*)") +VERSION = re.compile(rf"{CORE}(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?") + + +def resolve(tags, ref_type, ref_name): + if ref_type == "tag": + if not ref_name.startswith("v"): + raise ValueError("Release tags must start with v") + version = ref_name[1:] + elif ref_type == "branch" and ref_name == "main": + stable = [tuple(map(int, tag[1:].split("."))) for tag in tags + if tag.startswith("v") and STABLE.fullmatch(tag[1:])] + latest = max(stable, default=(0, 0, 0)) + version = f"{latest[0]}.{latest[1]}.{latest[2] + 1}-SNAPSHOT" + else: + raise ValueError("Releases require a version tag; automatic snapshots use main") + match = VERSION.fullmatch(version) + if not match or (match[1] and any(part.isascii() and part.isdigit() and len(part) > 1 and part.startswith("0") + for part in match[1].split("."))): + raise ValueError(f"Invalid release version: {version}") + return { + "version": version, + "is_snapshot": str(version.endswith("-SNAPSHOT")).lower(), + "is_prerelease": str(match[1] is not None).lower(), + "test_release": str(RC.fullmatch(version) is not None).lower(), + "release_tag": f"v{version}", + } + + +if __name__ == "__main__": + tags = subprocess.check_output(["git", "tag", "--list"], text=True).splitlines() + try: + values = resolve(tags, os.environ["GITHUB_REF_TYPE"], os.environ["GITHUB_REF_NAME"]) + except ValueError as error: + raise SystemExit(str(error)) from error + values["revision"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output: + output.writelines(f"{key}={value}\n" for key, value in values.items()) + print(f"Resolved {values['version']}; GitHub-only RC: {values['test_release']}") diff --git a/.github/scripts/test-release-version.py b/.github/scripts/test-release-version.py new file mode 100644 index 0000000..2ba9f48 --- /dev/null +++ b/.github/scripts/test-release-version.py @@ -0,0 +1,45 @@ +"""Exercise exact release-tag routing and automatic main snapshots.""" + +import importlib.util +import unittest +from pathlib import Path + +spec = importlib.util.spec_from_file_location("versions", Path(__file__).with_name("release-version.py")) +versions = importlib.util.module_from_spec(spec) +spec.loader.exec_module(versions) + + +class ReleaseVersionTest(unittest.TestCase): + def test_main_snapshot_ignores_prerelease_tags(self): + tags = ["v1.9.0", "v1.10.0", "v2.0.0-rc.9"] + result = versions.resolve(tags, "branch", "main") + self.assertEqual("1.10.1-SNAPSHOT", result["version"]) + self.assertEqual("true", result["is_snapshot"]) + self.assertEqual("false", result["test_release"]) + self.assertEqual("0.0.1-SNAPSHOT", versions.resolve([], "branch", "main")["version"]) + + def test_rc_tag_is_used_exactly_without_incrementing(self): + result = versions.resolve(["v1.3.0", "v1.3.1-rc.10"], "tag", "v1.3.1-rc.2") + self.assertEqual("1.3.1-rc.2", result["version"]) + self.assertEqual("v1.3.1-rc.2", result["release_tag"]) + self.assertEqual("true", result["test_release"]) + self.assertEqual("true", result["is_prerelease"]) + self.assertEqual("false", result["is_snapshot"]) + + def test_stable_tag_routes_to_central(self): + result = versions.resolve(["v1.3.0"], "tag", "v1.4.0") + self.assertEqual("1.4.0", result["version"]) + self.assertEqual("false", result["test_release"]) + self.assertEqual("false", result["is_prerelease"]) + self.assertEqual("false", result["is_snapshot"]) + + def test_invalid_tags_and_non_main_branches_fail(self): + for tag in ["v01.3.1", "v1.3.1-rc.01", "v1.3.1-rc..1", "v1.3.1-", "1.3.1"]: + with self.subTest(tag=tag), self.assertRaises(ValueError): + versions.resolve([], "tag", tag) + with self.assertRaisesRegex(ValueError, "require a version tag"): + versions.resolve([], "branch", "feature") + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/test-verification.py b/.github/scripts/test-verification.py new file mode 100644 index 0000000..e71e64b --- /dev/null +++ b/.github/scripts/test-verification.py @@ -0,0 +1,41 @@ +"""Checks that the CI report guard rejects green builds without passing tests.""" + +import importlib.util +import tempfile +import unittest +from pathlib import Path + +spec = importlib.util.spec_from_file_location("reports", Path(__file__).with_name("verify-test-results.py")) +reports = importlib.util.module_from_spec(spec) +spec.loader.exec_module(reports) + + +class ReportVerificationTest(unittest.TestCase): + def test_missing_reports(self): + with tempfile.TemporaryDirectory() as directory: + with self.assertRaisesRegex(ValueError, "No test reports"): + reports.verify(directory) + + def test_rejects_empty_skipped_and_failing_results(self): + examples = [ + '', + '', + '', + '', + ] + for xml in examples: + with self.subTest(xml=xml), tempfile.TemporaryDirectory() as directory: + Path(directory, "TEST-example.xml").write_text(xml) + with self.assertRaises(ValueError): + reports.verify(directory) + + def test_accepts_executed_tests_alongside_skipped_tests(self): + with tempfile.TemporaryDirectory() as directory: + Path(directory, "TEST-example.xml").write_text( + '' + ) + reports.verify(directory) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/scripts/verify-consumer.sh b/.github/scripts/verify-consumer.sh new file mode 100644 index 0000000..fc5d544 --- /dev/null +++ b/.github/scripts/verify-consumer.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# The fixture deliberately uses no KBuild conventions or build platform. +set -euo pipefail +version=${1:?usage: verify-consumer.sh VERSION} +repository="$(pwd)/build/staging-repo" +./gradlew -p consumer run "-Pconsumer.repository=$repository" "-Pconsumer.version=$version" +./gradlew -p consumer run "-Pconsumer.repository=$repository" "-Pconsumer.version=$version" -Pconsumer.pomOnly=true +if [[ $# -gt 1 ]]; then + ./gradlew -p consumer run "-Pconsumer.version=$version" "-Pconsumer.githubRepository=$2" +fi diff --git a/.github/scripts/verify-publication.py b/.github/scripts/verify-publication.py new file mode 100644 index 0000000..5d7168b --- /dev/null +++ b/.github/scripts/verify-publication.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Verify the complete staged KMP publication before independent consumer resolution.""" + +import argparse +import hashlib +import json +import re +import xml.etree.ElementTree as ET +from pathlib import Path +from zipfile import ZipFile + + +def verify(repository, version, full=False, github=False): + settings = Path(__file__).resolve().parents[2] / "settings.gradle.kts" + library = re.search(r'rootProject\.name\s*=\s*"([^"\n]+)"', settings.read_text()).group(1) + repository = Path(repository).resolve() + base = repository if github else repository / "com/eignex" + targets = ["jvm", "linuxx64"] + if full: + targets += ["js", "wasm-js", "wasm-wasi", "linuxarm64", "macosarm64", "mingwx64", "iosx64", "iosarm64", "iossimulatorarm64"] + modules = [library] + [library + "-" + target for target in targets] + for module in modules: + directory = base if github else base / module / version + stem = f"{module}-{version}" + metadata = directory / f"{stem}.module" + pom = directory / f"{stem}.pom" + data = json.loads(metadata.read_text()) + ET.parse(pom) + if module == library: + published = {variant["available-at"]["module"] for variant in data["variants"] if "available-at" in variant} + if published != set(modules[1:]): + raise ValueError(f"Publication targets do not match the requested profile: {sorted(published)}") + if "kbuild-platform" in metadata.read_text() or "kbuild-platform" in pom.read_text(): + raise ValueError(f"Build platform leaked into {module}") + for variant in data["variants"]: + redirect = variant.get("available-at") + if redirect and not (directory / redirect["url"]).is_file(): + raise ValueError(f"Missing target metadata: {redirect['url']}") + for artifact in variant.get("files", []): + path = directory / artifact["url"] + if not path.is_file() or path.stat().st_size != artifact["size"]: + raise ValueError(f"Missing or truncated artifact: {path}") + if hashlib.sha256(path.read_bytes()).hexdigest() != artifact["sha256"]: + raise ValueError(f"Artifact checksum mismatch: {path}") + for dependency in variant.get("dependencies", []): + if not any(dependency.get("version", {}).get(key) for key in ("requires", "strictly", "prefers")): + raise ValueError(f"Versionless dependency in {module}: {dependency}") + for classifier in ("sources", "javadoc"): + with ZipFile(directory / f"{stem}-{classifier}.jar") as archive: + if not any(name != "META-INF/MANIFEST.MF" and not name.endswith("/") for name in archive.namelist()): + raise ValueError(f"Empty {classifier} artifact for {module}") + if module == library + "-jvm": + for variant in data["variants"]: + if (variant.get("attributes", {}).get("org.gradle.category") == "library" + and variant["attributes"].get("org.gradle.usage") in ("java-api", "java-runtime")): + if variant["attributes"].get("org.gradle.jvm.version") != 17: + raise ValueError("Published JVM requirement must remain Java 17") + with ZipFile(directory / f"{stem}.jar") as archive: + for name in archive.namelist(): + if name.endswith(".class") and int.from_bytes(archive.read(name)[6:8], "big") > 61: + raise ValueError(f"Bytecode above Java 17: {name}") + if github: + checksums = dict(line.split(" ", 1)[::-1] for line in (base / "SHA256SUMS").read_text().splitlines()) + files = {path.name for path in base.iterdir() if path.is_file() and path.name != "SHA256SUMS"} + if set(checksums) != files: + raise ValueError("GitHub checksum manifest does not cover every release asset") + for name, digest in checksums.items(): + if hashlib.sha256((base / name).read_bytes()).hexdigest() != digest: + raise ValueError(f"GitHub asset checksum mismatch: {name}") + print(f"Verified {len(modules)} complete publications at {version}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("repository") + parser.add_argument("version") + parser.add_argument("--full", action="store_true", help="Require the complete release target set") + parser.add_argument("--github", action="store_true", help="Verify flat GitHub Release assets and their checksum manifest") + args = parser.parse_args() + verify(args.repository, args.version, args.full, args.github) diff --git a/.github/scripts/verify-test-results.py b/.github/scripts/verify-test-results.py new file mode 100644 index 0000000..557a002 --- /dev/null +++ b/.github/scripts/verify-test-results.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Reject missing, empty, fully skipped, or failing expected test targets.""" + +import sys +import xml.etree.ElementTree as ET +from pathlib import Path + + +def verify(directory): + reports = sorted(Path(directory).glob("TEST-*.xml")) + if not reports: + raise ValueError(f"No test reports in {directory}") + executed = 0 + for report in reports: + suite = ET.parse(report).getroot() + cases = list(suite.iter("testcase")) + if (int(suite.get("failures", 0)) or int(suite.get("errors", 0)) + or any(case.find("failure") is not None or case.find("error") is not None for case in cases)): + raise ValueError(f"Failing test report: {report}") + executed += sum(case.find("skipped") is None for case in cases) + if not executed: + raise ValueError(f"No executed tests in {directory}") + print(f"Verified {executed} tests in {directory}") + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit("usage: verify-test-results.py DIRECTORY...") + try: + for directory in sys.argv[1:]: + verify(directory) + except (ValueError, ET.ParseError) as error: + sys.exit(str(error)) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20b6a39..cabba7e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,11 +5,23 @@ on: branches: [ main ] pull_request: workflow_dispatch: + inputs: + fullTargets: + description: Validate all publication targets and extra native hosts + type: boolean + default: false workflow_call: inputs: ref: type: string default: '' + ciVersion: + description: Version to stage and verify; releases use their actual version + type: string + default: '0.0.0-ci' + fullTargets: + type: boolean + default: false secrets: GRADLE_ENCRYPTION_KEY: required: false @@ -38,34 +50,58 @@ jobs: persist-credentials: false fetch-depth: 2 - - name: Set up JDK - uses: actions/setup-java@v6 + - name: Set up build + uses: ./.github/actions/setup-build with: - distribution: temurin - java-version: 25 - - # Build jobs persist dependency and encrypted configuration caches. - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: false cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + target-profile: ${{ inputs.fullTargets && 'full' || 'default' }} - - name: Cache Kotlin/Native toolchain - uses: actions/cache@v6 - with: - path: ~/.konan - key: ${{ runner.os }}-${{ runner.arch }}-konan-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', '**/gradle-wrapper.properties', '**/*.versions.toml') }} - restore-keys: ${{ runner.os }}-${{ runner.arch }}-konan- + - name: Validate CI guards + run: | + python3 .github/scripts/test-verification.py + python3 .github/scripts/test-release-version.py - - name: Grant execute permission for Gradle - run: chmod +x gradlew + # Routine builds use a stable private version for configuration-cache reuse. + # Releases rerun this gate at their actual publication version. + # The independent consumer treats only this local artifact as changing. + # One graph shares compilation, lint, docs and publication artifacts. + - name: Check library and stage publications + env: + CONSUMER_VERSION: ${{ inputs.ciVersion || '0.0.0-ci' }} + FULL_TARGETS: ${{ inputs.fullTargets || false }} + run: | + target_args=() + if [[ "$FULL_TARGETS" == true ]]; then target_args+=(-Ptargets.full); fi + ./gradlew check lintDocs koverXmlReport publishAllPublicationsToLocalStagingRepository \ + -PciVersion="$CONSUMER_VERSION" "${target_args[@]}" - - name: Run linter - run: ./gradlew lintDocs + - name: Require executed tests for the selected target set + env: + FULL_TARGETS: ${{ inputs.fullTargets || false }} + run: | + reports=(build/test-results/jvmTest build/test-results/jvmTestJarOnJava17 + build/test-results/jvmTestJarOnJava25 build/test-results/linuxX64Test) + if [[ "$FULL_TARGETS" == true ]]; then + reports+=(build/test-results/jsBrowserTest build/test-results/jsNodeTest + build/test-results/wasmJsBrowserTest build/test-results/wasmJsNodeTest + build/test-results/wasmWasiNodeTest) + fi + python3 .github/scripts/verify-test-results.py "${reports[@]}" - - name: Run tests with coverage - run: ./gradlew check koverXmlReport + - name: Verify staged publications and independent consumers + env: + CONSUMER_VERSION: ${{ inputs.ciVersion || '0.0.0-ci' }} + FULL_TARGETS: ${{ inputs.fullTargets || false }} + run: | + target_args=() + if [[ "$FULL_TARGETS" == true ]]; then target_args+=(--full); fi + python3 .github/scripts/verify-publication.py build/staging-repo "$CONSUMER_VERSION" "${target_args[@]}" + bash .github/scripts/verify-consumer.sh "$CONSUMER_VERSION" + + - name: Require committed JavaScript and Wasm lockfiles + run: | + git diff --exit-code -- kotlin-js-store + test -z "$(git ls-files --others --exclude-standard -- kotlin-js-store)" - name: Upload test reports if: always() @@ -84,3 +120,57 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: build/reports/kover/report.xml fail_ci_if_error: false + + native: + if: ${{ inputs.fullTargets == true }} + name: Native tests (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + include: + - os: macos-15 + target: macosArm64 + python: python3 + - os: windows-latest + target: mingwX64 + python: python + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref || github.sha }} + persist-credentials: false + - name: Set up build + uses: ./.github/actions/setup-build + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + target-profile: full + # Native checks only: lint, coverage and web tests belong to the Linux job. + - name: Run native tests + shell: bash + run: ./gradlew ${{ matrix.target }}Test -Ptargets.full --max-workers=2 -Pkotlin.native.parallelThreads=2 + - name: Require executed native tests + shell: bash + run: ${{ matrix.python }} .github/scripts/verify-test-results.py build/test-results/${{ matrix.target }}Test + # Extra native hosts and simulator linking are part of explicit full validation only. + - name: Run iOS simulator tests + if: runner.os == 'macOS' + shell: bash + run: | + device=$(xcrun simctl list devices available --json | python3 -c ' + import json, sys + devices = json.load(sys.stdin)["devices"] + print(next(d["udid"] for runtime, items in devices.items() if ".iOS-" in runtime + for d in items if d["name"].startswith("iPhone")))') + ./gradlew iosSimulatorArm64Test -Ptargets.full --max-workers=2 -Pkotlin.native.parallelThreads=2 -PiosSimulatorDevice="$device" + python3 .github/scripts/verify-test-results.py build/test-results/iosSimulatorArm64Test + - name: Upload native test reports + if: always() + uses: actions/upload-artifact@v7 + with: + name: test-reports-${{ matrix.target }} + path: | + build/reports/tests/ + build/test-results/ + if-no-files-found: ignore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa53edb..8655118 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,12 +7,6 @@ on: branches: [main] push: tags: ['v*'] - workflow_dispatch: - inputs: - dryRun: - description: Run the release build and publish nothing - type: boolean - default: false permissions: contents: read @@ -36,7 +30,8 @@ jobs: version: ${{ steps.version.outputs.version }} is_snapshot: ${{ steps.version.outputs.is_snapshot }} is_prerelease: ${{ steps.version.outputs.is_prerelease }} - dry_run: ${{ steps.version.outputs.dry_run }} + test_release: ${{ steps.version.outputs.test_release }} + release_tag: ${{ steps.version.outputs.release_tag }} revision: ${{ steps.version.outputs.revision }} steps: - name: Checkout @@ -47,52 +42,18 @@ jobs: fetch-depth: 0 - name: Resolve publication version id: version - env: - DRY_RUN: ${{ inputs.dryRun || false }} - run: | - next_patch() { - local tag major minor patch - while IFS= read -r tag; do - if [[ "$tag" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then - major=${BASH_REMATCH[1]} - minor=${BASH_REMATCH[2]} - patch=${BASH_REMATCH[3]} - printf '%s.%s.%s\n' "$major" "$minor" "$((10#$patch + 1))" - return - fi - done < <(git tag --list 'v*' --sort=-v:refname) - echo '0.0.1' - } - if [[ "$DRY_RUN" == true ]]; then - version=$(next_patch) - elif [[ "$GITHUB_REF_TYPE" == tag ]]; then - version=${GITHUB_REF_NAME#v} - else - version="$(next_patch)-SNAPSHOT" - fi - if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then - echo "::error::invalid publication version: $version" >&2 - exit 1 - fi - is_snapshot=false - [[ "$version" != *-SNAPSHOT ]] || is_snapshot=true - is_prerelease=false - [[ "$version" != *-* ]] || is_prerelease=true - { - echo "version=$version" - echo "is_snapshot=$is_snapshot" - echo "is_prerelease=$is_prerelease" - echo "dry_run=$DRY_RUN" - echo "revision=$(git rev-parse HEAD)" - } >> "$GITHUB_OUTPUT" + run: python3 .github/scripts/release-version.py - # Automatic snapshots already passed this gate. + # Releases rerun Linux checks at the release version alongside macOS/iOS and Windows. + # Publication waits for every platform. Snapshots reuse the normal main build. validate: if: github.event_name != 'workflow_run' needs: prepare uses: ./.github/workflows/build.yml with: ref: ${{ needs.prepare.outputs.revision }} + ciVersion: ${{ needs.prepare.outputs.version }} + fullTargets: true secrets: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} @@ -110,60 +71,70 @@ jobs: env: RELEASE_VERSION: ${{ needs.prepare.outputs.version }} IS_SNAPSHOT: ${{ needs.prepare.outputs.is_snapshot }} - DRY_RUN: ${{ needs.prepare.outputs.dry_run }} + IS_TEST_RELEASE: ${{ needs.prepare.outputs.test_release }} + RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }} steps: - name: Checkout uses: actions/checkout@v7 with: ref: ${{ needs.prepare.outputs.revision }} persist-credentials: false - - name: Set up JDK - uses: actions/setup-java@v6 + - name: Set up build + uses: ./.github/actions/setup-build with: - distribution: temurin - java-version: 25 - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v6 - with: - cache-read-only: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - name: Restore Kotlin/Native toolchain - uses: actions/cache/restore@v6 - with: - path: ~/.konan - key: ${{ runner.os }}-${{ runner.arch }}-konan-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', '**/gradle-wrapper.properties', '**/*.versions.toml') }} - restore-keys: ${{ runner.os }}-${{ runner.arch }}-konan- + gradle-cache-read-only: 'true' + target-profile: full - - name: Build and Test + # The reusable validation job already ran the full checks and uploaded test reports. + # Recheck the publication job's staged artifacts and consumer resolution before publishing. + - name: Verify release publication if: env.IS_SNAPSHOT == 'false' - run: ./gradlew build -PciVersion="$RELEASE_VERSION" - - name: Upload test reports - if: always() && env.IS_SNAPSHOT == 'false' + run: | + ./gradlew publishAllPublicationsToLocalStagingRepository -Ptargets.full -PciVersion="$RELEASE_VERSION" + python3 .github/scripts/verify-publication.py build/staging-repo "$RELEASE_VERSION" --full + bash .github/scripts/verify-consumer.sh "$RELEASE_VERSION" + - name: Stage and verify GitHub RC assets + if: env.IS_TEST_RELEASE == 'true' + run: | + ./gradlew githubReleaseAssets -Ptargets.full -PciVersion="$RELEASE_VERSION" + python3 .github/scripts/verify-publication.py build/github-release-assets "$RELEASE_VERSION" --full --github + bash .github/scripts/verify-consumer.sh "$RELEASE_VERSION" "$PWD/build/github-release-assets" + - name: Upload validated RC assets for inspection + if: env.IS_TEST_RELEASE == 'true' uses: actions/upload-artifact@v7 with: - name: release-test-reports - path: | - **/build/reports/tests/ - **/build/test-results/ - if-no-files-found: ignore + name: test-release-assets + path: build/github-release-assets/ + if-no-files-found: error + - name: Publish RC to GitHub only + if: env.IS_TEST_RELEASE == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: ./gradlew publishToGitHubRelease -Ptargets.full -PciVersion="$RELEASE_VERSION" + - name: Verify anonymous consumer from GitHub + if: env.IS_TEST_RELEASE == 'true' + run: >- + ./gradlew -p consumer run -Pconsumer.version="$RELEASE_VERSION" + -Pconsumer.githubRepository="https://github.com/$GITHUB_REPOSITORY/releases/download/$RELEASE_TAG" - name: Publish to Maven Central (Portal) - if: env.IS_SNAPSHOT == 'false' && env.DRY_RUN != 'true' + if: env.IS_SNAPSHOT == 'false' && env.IS_TEST_RELEASE != 'true' env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }} - run: ./gradlew publishToMavenCentral -PciVersion="$RELEASE_VERSION" + run: ./gradlew publishToMavenCentral -Ptargets.full -PciVersion="$RELEASE_VERSION" - name: Publish to Sonatype Snapshots - if: env.IS_SNAPSHOT == 'true' && env.DRY_RUN != 'true' + if: env.IS_SNAPSHOT == 'true' env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }} - run: ./gradlew publishAllPublicationsToSonatypeSnapshotsRepository -PciVersion="$RELEASE_VERSION" + run: ./gradlew publishAllPublicationsToSonatypeSnapshotsRepository -Ptargets.full -PciVersion="$RELEASE_VERSION" - name: Create GitHub release - if: github.ref_type == 'tag' && env.IS_SNAPSHOT == 'false' && env.DRY_RUN != 'true' + if: github.ref_type == 'tag' && env.IS_SNAPSHOT == 'false' && env.IS_TEST_RELEASE != 'true' uses: softprops/action-gh-release@v3 with: prerelease: ${{ needs.prepare.outputs.is_prerelease }} diff --git a/.gitignore b/.gitignore index 3ed6d5a..27d164e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ out/ .specstory/ .aider* *.local.md + +### Python CI helpers ### +__pycache__/ diff --git a/README.md b/README.md index ada402c..829d670 100644 --- a/README.md +++ b/README.md @@ -109,3 +109,50 @@ Here is what the text "any byte data" (13 bytes) looks like in each: For even shorter strings, BaseRadix can use a large range of Unicode characters. + +## Build validation + +`./gradlew check lintDocs koverXmlReport` checks JVM and Linux x64. +JS, Wasm, WASI and other native targets are configured only with `-Ptargets.full`. JVM checks also +run the packaged JAR on Java 17 and 25. CI stages the configured publications, +checks their metadata and artifacts, and runs an independent Java consumer using +both Gradle metadata and POM-only resolution. + +Normal pull-request and main builds use one Linux job. Tagged stable and RC releases +enable `-Ptargets.full`, with focused macOS ARM64, iOS simulator and Windows checks. +The build workflow's manual `fullTargets` input runs the same gate without publishing. +Automatic snapshots publish all targets without repeating the full runtime gate. +Releases rerun the full Linux gate at the actual release version, in parallel with +the macOS/iOS and Windows jobs. Publication waits for every platform to pass and +rechecks its staged artifacts and independent consumers before publishing. + +ABI references live in `api/` for normal checks and `api-full/` for full checks. +When intentionally updating the API, run `./gradlew updateKotlinAbi` and +`./gradlew updateKotlinAbi -Ptargets.full`. JavaScript and Wasm dependency lockfiles +under `kotlin-js-store/` must be committed; use +`./gradlew kotlinUpgradeYarnLock kotlinWasmUpgradeYarnLock -Ptargets.full` to update them. + +CI caches Gradle dependency/task outputs, the host-specific Native toolchain, and +Gradle-encrypted configuration state separately. Configuration state is persisted +only when the encryption secret is available. The local CI publication uses a +stable version so source-only commits can reuse configuration state; the consumer +always refreshes that local module, while external dependencies remain cached. + +### Tagged releases + +Push an explicit SemVer RC tag to run the full parallel release gate and publish +all 12 publications with `SHA256SUMS` to a GitHub prerelease: + +```sh +git tag v1.4.1-rc.1 +git push origin v1.4.1-rc.1 +``` + +The version comes directly from the tag. Release workflows never create tags or +increment RC numbers. RCs publish to GitHub only; stable tags such as `v1.4.1` +publish to Maven Central and create a GitHub release after validation. +There is no manual release dispatch. + +GitHub RC assets include JVM/native/web artifacts, sources, documentation and +Gradle metadata. They can be consumed anonymously using the flat Ivy repository +pattern in [the independent consumer](consumer/settings.gradle.kts). diff --git a/api-full/kencode.api b/api-full/kencode.api new file mode 100644 index 0000000..fd5f173 --- /dev/null +++ b/api-full/kencode.api @@ -0,0 +1,323 @@ +public abstract interface class com/eignex/kencode/Alphabet { + public abstract fun get (I)C + public abstract fun getSize ()I + public abstract fun indexOf (C)I +} + +public final class com/eignex/kencode/Base36 : com/eignex/kencode/BaseRadix { + public static final field INSTANCE Lcom/eignex/kencode/Base36; +} + +public final class com/eignex/kencode/Base62 : com/eignex/kencode/BaseRadix { + public static final field INSTANCE Lcom/eignex/kencode/Base62; +} + +public class com/eignex/kencode/Base64 : com/eignex/kencode/ByteEncoding { + public static final field Default Lcom/eignex/kencode/Base64$Default; + public fun ([C)V + public fun decode (Ljava/lang/CharSequence;)[B + public fun encode ([BII)Ljava/lang/String; + public final fun getAlphabet ()[C +} + +public final class com/eignex/kencode/Base64$Default : com/eignex/kencode/Base64 { +} + +public final class com/eignex/kencode/Base64Kt { + public static final field BASE_64 Ljava/lang/String; + public static final field BASE_64_URL Ljava/lang/String; +} + +public final class com/eignex/kencode/Base64Url : com/eignex/kencode/Base64 { + public static final field INSTANCE Lcom/eignex/kencode/Base64Url; +} + +public class com/eignex/kencode/Base85 : com/eignex/kencode/ByteEncoding { + public static final field Default Lcom/eignex/kencode/Base85$Default; + public fun ([C)V + public fun decode (Ljava/lang/CharSequence;)[B + public fun encode ([BII)Ljava/lang/String; +} + +public final class com/eignex/kencode/Base85$Default : com/eignex/kencode/Base85 { +} + +public final class com/eignex/kencode/Base85Kt { + public static final field ASCII85 Ljava/lang/String; +} + +public class com/eignex/kencode/BaseRadix : com/eignex/kencode/ByteEncoding { + public fun (Lcom/eignex/kencode/Alphabet;I)V + public synthetic fun (Lcom/eignex/kencode/Alphabet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;I)V + public synthetic fun (Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun decode (Ljava/lang/CharSequence;)[B + public fun encode ([BII)Ljava/lang/String; + public final fun getBlockSize ()I +} + +public final class com/eignex/kencode/BaseRadixKt { + public static final field BASE_62 Ljava/lang/String; +} + +public abstract interface class com/eignex/kencode/ByteEncoding { + public abstract fun decode (Ljava/lang/CharSequence;)[B + public abstract fun encode ([BII)Ljava/lang/String; + public static synthetic fun encode$default (Lcom/eignex/kencode/ByteEncoding;[BIIILjava/lang/Object;)Ljava/lang/String; +} + +public final class com/eignex/kencode/ByteEncoding$DefaultImpls { + public static synthetic fun encode$default (Lcom/eignex/kencode/ByteEncoding;[BIIILjava/lang/Object;)Ljava/lang/String; +} + +public final class com/eignex/kencode/CharAlphabet : com/eignex/kencode/Alphabet { + public fun (Ljava/lang/String;)V + public fun get (I)C + public fun getSize ()I + public fun indexOf (C)I +} + +public abstract interface class com/eignex/kencode/Checksum { + public abstract fun digest ([B)[B + public abstract fun getSize ()I +} + +public final class com/eignex/kencode/CompactZeros : com/eignex/kencode/PayloadTransform { + public static final field INSTANCE Lcom/eignex/kencode/CompactZeros; + public fun decode ([B)[B + public fun encode ([B)[B +} + +public abstract class com/eignex/kencode/Crc : com/eignex/kencode/Checksum { + public fun digest ([B)[B + public fun getSize ()I +} + +public class com/eignex/kencode/Crc16 : com/eignex/kencode/Crc { + public static final field Default Lcom/eignex/kencode/Crc16$Default; + public fun ()V + public fun (IIZZI)V + public synthetic fun (IIZZIILkotlin/jvm/internal/DefaultConstructorMarker;)V +} + +public final class com/eignex/kencode/Crc16$Default : com/eignex/kencode/Crc16 { +} + +public class com/eignex/kencode/Crc32 : com/eignex/kencode/Crc { + public static final field Default Lcom/eignex/kencode/Crc32$Default; + public fun ()V + public fun (IIZZI)V + public synthetic fun (IIZZIILkotlin/jvm/internal/DefaultConstructorMarker;)V +} + +public final class com/eignex/kencode/Crc32$Default : com/eignex/kencode/Crc32 { +} + +public class com/eignex/kencode/Crc8 : com/eignex/kencode/Crc { + public static final field Default Lcom/eignex/kencode/Crc8$Default; + public fun ()V + public fun (IIZZI)V + public synthetic fun (IIZZIILkotlin/jvm/internal/DefaultConstructorMarker;)V +} + +public final class com/eignex/kencode/Crc8$Default : com/eignex/kencode/Crc8 { +} + +public final class com/eignex/kencode/EncodedConfiguration { + public fun ()V + public fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;)V + public synthetic fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/eignex/kencode/ByteEncoding; + public final fun component2 ()Lcom/eignex/kencode/PayloadTransform; + public final fun component3 ()Lkotlinx/serialization/BinaryFormat; + public final fun copy (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;)Lcom/eignex/kencode/EncodedConfiguration; + public static synthetic fun copy$default (Lcom/eignex/kencode/EncodedConfiguration;Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;ILjava/lang/Object;)Lcom/eignex/kencode/EncodedConfiguration; + public fun equals (Ljava/lang/Object;)Z + public final fun getBinaryFormat ()Lkotlinx/serialization/BinaryFormat; + public final fun getCodec ()Lcom/eignex/kencode/ByteEncoding; + public final fun getTransform ()Lcom/eignex/kencode/PayloadTransform; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public class com/eignex/kencode/EncodedFormat : kotlinx/serialization/StringFormat { + public static final field Default Lcom/eignex/kencode/EncodedFormat$Default; + public fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;)V + public synthetic fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lcom/eignex/kencode/EncodedConfiguration;)V + public fun decodeFromString (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object; + public fun encodeToString (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String; + public final fun getConfiguration ()Lcom/eignex/kencode/EncodedConfiguration; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; +} + +public final class com/eignex/kencode/EncodedFormat$Default : com/eignex/kencode/EncodedFormat { +} + +public final class com/eignex/kencode/EncodedFormatBuilder { + public fun ()V + public final fun getBinaryFormat ()Lkotlinx/serialization/BinaryFormat; + public final fun getChecksum ()Lcom/eignex/kencode/Checksum; + public final fun getCodec ()Lcom/eignex/kencode/ByteEncoding; + public final fun getTransform ()Lcom/eignex/kencode/PayloadTransform; + public final fun setBinaryFormat (Lkotlinx/serialization/BinaryFormat;)V + public final fun setChecksum (Lcom/eignex/kencode/Checksum;)V + public final fun setCodec (Lcom/eignex/kencode/ByteEncoding;)V + public final fun setTransform (Lcom/eignex/kencode/PayloadTransform;)V +} + +public final class com/eignex/kencode/EncodedFormatKt { + public static final fun EncodedFormat (Lcom/eignex/kencode/EncodedFormat;Lkotlin/jvm/functions/Function1;)Lcom/eignex/kencode/EncodedFormat; + public static synthetic fun EncodedFormat$default (Lcom/eignex/kencode/EncodedFormat;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/eignex/kencode/EncodedFormat; +} + +public final class com/eignex/kencode/IntPacking : java/lang/Enum { + public static final field DEFAULT Lcom/eignex/kencode/IntPacking; + public static final field FIXED Lcom/eignex/kencode/IntPacking; + public static final field SIGNED Lcom/eignex/kencode/IntPacking; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/eignex/kencode/IntPacking; + public static fun values ()[Lcom/eignex/kencode/IntPacking; +} + +public final class com/eignex/kencode/PackedConfiguration { + public fun ()V + public fun (Lcom/eignex/kencode/IntPacking;)V + public synthetic fun (Lcom/eignex/kencode/IntPacking;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/eignex/kencode/IntPacking; + public final fun copy (Lcom/eignex/kencode/IntPacking;)Lcom/eignex/kencode/PackedConfiguration; + public static synthetic fun copy$default (Lcom/eignex/kencode/PackedConfiguration;Lcom/eignex/kencode/IntPacking;ILjava/lang/Object;)Lcom/eignex/kencode/PackedConfiguration; + public fun equals (Ljava/lang/Object;)Z + public final fun getDefaultEncoding ()Lcom/eignex/kencode/IntPacking; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/eignex/kencode/PackedDecoder : kotlinx/serialization/encoding/CompositeDecoder, kotlinx/serialization/encoding/Decoder { + public fun ([BLcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;)V + public synthetic fun ([BLcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder; + public fun decodeBoolean ()Z + public fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z + public fun decodeByte ()B + public fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B + public fun decodeChar ()C + public fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C + public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I + public fun decodeDouble ()D + public fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D + public fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I + public fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I + public fun decodeFloat ()F + public fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F + public fun decodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Decoder; + public fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder; + public fun decodeInt ()I + public fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I + public fun decodeLong ()J + public fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J + public fun decodeNotNullMark ()Z + public fun decodeNull ()Ljava/lang/Void; + public fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; + public fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; + public fun decodeSequentially ()Z + public fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; + public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; + public fun decodeShort ()S + public fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S + public fun decodeString ()Ljava/lang/String; + public fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String; + public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; +} + +public final class com/eignex/kencode/PackedEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder { + public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; + public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder; + public fun encodeBoolean (Z)V + public fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V + public fun encodeByte (B)V + public fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V + public fun encodeChar (C)V + public fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V + public fun encodeDouble (D)V + public fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V + public fun encodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;I)V + public fun encodeFloat (F)V + public fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V + public fun encodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Encoder; + public fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder; + public fun encodeInt (I)V + public fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V + public fun encodeLong (J)V + public fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V + public fun encodeNotNullMark ()V + public fun encodeNull ()V + public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public fun encodeShort (S)V + public fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V + public fun encodeString (Ljava/lang/String;)V + public fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V + public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z +} + +public class com/eignex/kencode/PackedFormat : kotlinx/serialization/BinaryFormat { + public static final field Default Lcom/eignex/kencode/PackedFormat$Default; + public fun ()V + public fun (Lcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;)V + public synthetic fun (Lcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun decodeFromByteArray (Lkotlinx/serialization/DeserializationStrategy;[B)Ljava/lang/Object; + public fun encodeToByteArray (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)[B + public final fun getConfiguration ()Lcom/eignex/kencode/PackedConfiguration; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; +} + +public final class com/eignex/kencode/PackedFormat$Default : com/eignex/kencode/PackedFormat { +} + +public final class com/eignex/kencode/PackedFormatBuilder { + public fun ()V + public final fun getDefaultEncoding ()Lcom/eignex/kencode/IntPacking; + public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public final fun setDefaultEncoding (Lcom/eignex/kencode/IntPacking;)V + public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V +} + +public final class com/eignex/kencode/PackedFormatKt { + public static final fun PackedFormat (Lcom/eignex/kencode/PackedFormat;Lkotlin/jvm/functions/Function1;)Lcom/eignex/kencode/PackedFormat; + public static synthetic fun PackedFormat$default (Lcom/eignex/kencode/PackedFormat;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/eignex/kencode/PackedFormat; +} + +public abstract interface annotation class com/eignex/kencode/PackedType : java/lang/annotation/Annotation { + public abstract fun type ()Lcom/eignex/kencode/IntPacking; +} + +public final synthetic class com/eignex/kencode/PackedType$Impl : com/eignex/kencode/PackedType { + public fun (Lcom/eignex/kencode/IntPacking;)V + public final synthetic fun type ()Lcom/eignex/kencode/IntPacking; +} + +public abstract interface class com/eignex/kencode/PayloadTransform { + public abstract fun decode ([B)[B + public abstract fun encode ([B)[B +} + +public final class com/eignex/kencode/PayloadTransformKt { + public static final fun asTransform (Lcom/eignex/kencode/Checksum;)Lcom/eignex/kencode/PayloadTransform; + public static final fun then (Lcom/eignex/kencode/PayloadTransform;Lcom/eignex/kencode/PayloadTransform;)Lcom/eignex/kencode/PayloadTransform; +} + +public final class com/eignex/kencode/UnicodeRangeAlphabet : com/eignex/kencode/Alphabet { + public fun ()V + public fun (II)V + public synthetic fun (IIILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun get (I)C + public fun getSize ()I + public fun indexOf (C)I +} + diff --git a/api-full/kencode.klib.api b/api-full/kencode.klib.api new file mode 100644 index 0000000..da1e16c --- /dev/null +++ b/api-full/kencode.klib.api @@ -0,0 +1,316 @@ +// Klib ABI Dump +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, mingwX64, wasmJs, wasmWasi] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +open annotation class com.eignex.kencode/PackedType : kotlin/Annotation { // com.eignex.kencode/PackedType|null[0] + constructor (com.eignex.kencode/IntPacking) // com.eignex.kencode/PackedType.|(com.eignex.kencode.IntPacking){}[0] + + final val type // com.eignex.kencode/PackedType.type|{}type[0] + final fun (): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedType.type.|(){}[0] +} + +final enum class com.eignex.kencode/IntPacking : kotlin/Enum { // com.eignex.kencode/IntPacking|null[0] + enum entry DEFAULT // com.eignex.kencode/IntPacking.DEFAULT|null[0] + enum entry FIXED // com.eignex.kencode/IntPacking.FIXED|null[0] + enum entry SIGNED // com.eignex.kencode/IntPacking.SIGNED|null[0] + + final val entries // com.eignex.kencode/IntPacking.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // com.eignex.kencode/IntPacking.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): com.eignex.kencode/IntPacking // com.eignex.kencode/IntPacking.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // com.eignex.kencode/IntPacking.values|values#static(){}[0] +} + +abstract interface com.eignex.kencode/Alphabet { // com.eignex.kencode/Alphabet|null[0] + abstract val size // com.eignex.kencode/Alphabet.size|{}size[0] + abstract fun (): kotlin/Int // com.eignex.kencode/Alphabet.size.|(){}[0] + + abstract fun get(kotlin/Int): kotlin/Char // com.eignex.kencode/Alphabet.get|get(kotlin.Int){}[0] + abstract fun indexOf(kotlin/Char): kotlin/Int // com.eignex.kencode/Alphabet.indexOf|indexOf(kotlin.Char){}[0] +} + +abstract interface com.eignex.kencode/ByteEncoding { // com.eignex.kencode/ByteEncoding|null[0] + abstract fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/ByteEncoding.decode|decode(kotlin.CharSequence){}[0] + abstract fun encode(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...): kotlin/String // com.eignex.kencode/ByteEncoding.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] +} + +abstract interface com.eignex.kencode/Checksum { // com.eignex.kencode/Checksum|null[0] + abstract val size // com.eignex.kencode/Checksum.size|{}size[0] + abstract fun (): kotlin/Int // com.eignex.kencode/Checksum.size.|(){}[0] + + abstract fun digest(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/Checksum.digest|digest(kotlin.ByteArray){}[0] +} + +abstract interface com.eignex.kencode/PayloadTransform { // com.eignex.kencode/PayloadTransform|null[0] + abstract fun decode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/PayloadTransform.decode|decode(kotlin.ByteArray){}[0] + abstract fun encode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/PayloadTransform.encode|encode(kotlin.ByteArray){}[0] +} + +abstract class com.eignex.kencode/Crc : com.eignex.kencode/Checksum { // com.eignex.kencode/Crc|null[0] + open val size // com.eignex.kencode/Crc.size|{}size[0] + open fun (): kotlin/Int // com.eignex.kencode/Crc.size.|(){}[0] + + open fun digest(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/Crc.digest|digest(kotlin.ByteArray){}[0] +} + +final class com.eignex.kencode/CharAlphabet : com.eignex.kencode/Alphabet { // com.eignex.kencode/CharAlphabet|null[0] + constructor (kotlin/String) // com.eignex.kencode/CharAlphabet.|(kotlin.String){}[0] + + final val size // com.eignex.kencode/CharAlphabet.size|{}size[0] + final fun (): kotlin/Int // com.eignex.kencode/CharAlphabet.size.|(){}[0] + + final fun get(kotlin/Int): kotlin/Char // com.eignex.kencode/CharAlphabet.get|get(kotlin.Int){}[0] + final fun indexOf(kotlin/Char): kotlin/Int // com.eignex.kencode/CharAlphabet.indexOf|indexOf(kotlin.Char){}[0] +} + +final class com.eignex.kencode/EncodedConfiguration { // com.eignex.kencode/EncodedConfiguration|null[0] + constructor (com.eignex.kencode/ByteEncoding = ..., com.eignex.kencode/PayloadTransform? = ..., kotlinx.serialization/BinaryFormat = ...) // com.eignex.kencode/EncodedConfiguration.|(com.eignex.kencode.ByteEncoding;com.eignex.kencode.PayloadTransform?;kotlinx.serialization.BinaryFormat){}[0] + + final val binaryFormat // com.eignex.kencode/EncodedConfiguration.binaryFormat|{}binaryFormat[0] + final fun (): kotlinx.serialization/BinaryFormat // com.eignex.kencode/EncodedConfiguration.binaryFormat.|(){}[0] + final val codec // com.eignex.kencode/EncodedConfiguration.codec|{}codec[0] + final fun (): com.eignex.kencode/ByteEncoding // com.eignex.kencode/EncodedConfiguration.codec.|(){}[0] + final val transform // com.eignex.kencode/EncodedConfiguration.transform|{}transform[0] + final fun (): com.eignex.kencode/PayloadTransform? // com.eignex.kencode/EncodedConfiguration.transform.|(){}[0] + + final fun component1(): com.eignex.kencode/ByteEncoding // com.eignex.kencode/EncodedConfiguration.component1|component1(){}[0] + final fun component2(): com.eignex.kencode/PayloadTransform? // com.eignex.kencode/EncodedConfiguration.component2|component2(){}[0] + final fun component3(): kotlinx.serialization/BinaryFormat // com.eignex.kencode/EncodedConfiguration.component3|component3(){}[0] + final fun copy(com.eignex.kencode/ByteEncoding = ..., com.eignex.kencode/PayloadTransform? = ..., kotlinx.serialization/BinaryFormat = ...): com.eignex.kencode/EncodedConfiguration // com.eignex.kencode/EncodedConfiguration.copy|copy(com.eignex.kencode.ByteEncoding;com.eignex.kencode.PayloadTransform?;kotlinx.serialization.BinaryFormat){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // com.eignex.kencode/EncodedConfiguration.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // com.eignex.kencode/EncodedConfiguration.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // com.eignex.kencode/EncodedConfiguration.toString|toString(){}[0] +} + +final class com.eignex.kencode/EncodedFormatBuilder { // com.eignex.kencode/EncodedFormatBuilder|null[0] + constructor () // com.eignex.kencode/EncodedFormatBuilder.|(){}[0] + + final var binaryFormat // com.eignex.kencode/EncodedFormatBuilder.binaryFormat|{}binaryFormat[0] + final fun (): kotlinx.serialization/BinaryFormat // com.eignex.kencode/EncodedFormatBuilder.binaryFormat.|(){}[0] + final fun (kotlinx.serialization/BinaryFormat) // com.eignex.kencode/EncodedFormatBuilder.binaryFormat.|(kotlinx.serialization.BinaryFormat){}[0] + final var checksum // com.eignex.kencode/EncodedFormatBuilder.checksum|{}checksum[0] + final fun (): com.eignex.kencode/Checksum? // com.eignex.kencode/EncodedFormatBuilder.checksum.|(){}[0] + final fun (com.eignex.kencode/Checksum?) // com.eignex.kencode/EncodedFormatBuilder.checksum.|(com.eignex.kencode.Checksum?){}[0] + final var codec // com.eignex.kencode/EncodedFormatBuilder.codec|{}codec[0] + final fun (): com.eignex.kencode/ByteEncoding // com.eignex.kencode/EncodedFormatBuilder.codec.|(){}[0] + final fun (com.eignex.kencode/ByteEncoding) // com.eignex.kencode/EncodedFormatBuilder.codec.|(com.eignex.kencode.ByteEncoding){}[0] + final var transform // com.eignex.kencode/EncodedFormatBuilder.transform|{}transform[0] + final fun (): com.eignex.kencode/PayloadTransform? // com.eignex.kencode/EncodedFormatBuilder.transform.|(){}[0] + final fun (com.eignex.kencode/PayloadTransform?) // com.eignex.kencode/EncodedFormatBuilder.transform.|(com.eignex.kencode.PayloadTransform?){}[0] +} + +final class com.eignex.kencode/PackedConfiguration { // com.eignex.kencode/PackedConfiguration|null[0] + constructor (com.eignex.kencode/IntPacking = ...) // com.eignex.kencode/PackedConfiguration.|(com.eignex.kencode.IntPacking){}[0] + + final val defaultEncoding // com.eignex.kencode/PackedConfiguration.defaultEncoding|{}defaultEncoding[0] + final fun (): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedConfiguration.defaultEncoding.|(){}[0] + + final fun component1(): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedConfiguration.component1|component1(){}[0] + final fun copy(com.eignex.kencode/IntPacking = ...): com.eignex.kencode/PackedConfiguration // com.eignex.kencode/PackedConfiguration.copy|copy(com.eignex.kencode.IntPacking){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // com.eignex.kencode/PackedConfiguration.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // com.eignex.kencode/PackedConfiguration.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // com.eignex.kencode/PackedConfiguration.toString|toString(){}[0] +} + +final class com.eignex.kencode/PackedDecoder : kotlinx.serialization.encoding/CompositeDecoder, kotlinx.serialization.encoding/Decoder { // com.eignex.kencode/PackedDecoder|null[0] + constructor (kotlin/ByteArray, com.eignex.kencode/PackedConfiguration = ..., kotlinx.serialization.modules/SerializersModule = ...) // com.eignex.kencode/PackedDecoder.|(kotlin.ByteArray;com.eignex.kencode.PackedConfiguration;kotlinx.serialization.modules.SerializersModule){}[0] + + final val serializersModule // com.eignex.kencode/PackedDecoder.serializersModule|{}serializersModule[0] + final fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedDecoder.serializersModule.|(){}[0] + + final fun <#A1: kotlin/Any> decodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1?>, #A1?): #A1? // com.eignex.kencode/PackedDecoder.decodeNullableSerializableElement|decodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0?>;0:0?){0§}[0] + final fun <#A1: kotlin/Any?> decodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1>, #A1?): #A1 // com.eignex.kencode/PackedDecoder.decodeSerializableElement|decodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§}[0] + final fun <#A1: kotlin/Any?> decodeSerializableValue(kotlinx.serialization/DeserializationStrategy<#A1>): #A1 // com.eignex.kencode/PackedDecoder.decodeSerializableValue|decodeSerializableValue(kotlinx.serialization.DeserializationStrategy<0:0>){0§}[0] + final fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeDecoder // com.eignex.kencode/PackedDecoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun decodeBoolean(): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeBoolean|decodeBoolean(){}[0] + final fun decodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeBooleanElement|decodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeByte(): kotlin/Byte // com.eignex.kencode/PackedDecoder.decodeByte|decodeByte(){}[0] + final fun decodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Byte // com.eignex.kencode/PackedDecoder.decodeByteElement|decodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeChar(): kotlin/Char // com.eignex.kencode/PackedDecoder.decodeChar|decodeChar(){}[0] + final fun decodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Char // com.eignex.kencode/PackedDecoder.decodeCharElement|decodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeCollectionSize(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeCollectionSize|decodeCollectionSize(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun decodeDouble(): kotlin/Double // com.eignex.kencode/PackedDecoder.decodeDouble|decodeDouble(){}[0] + final fun decodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Double // com.eignex.kencode/PackedDecoder.decodeDoubleElement|decodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeElementIndex(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeElementIndex|decodeElementIndex(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun decodeEnum(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeEnum|decodeEnum(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun decodeFloat(): kotlin/Float // com.eignex.kencode/PackedDecoder.decodeFloat|decodeFloat(){}[0] + final fun decodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Float // com.eignex.kencode/PackedDecoder.decodeFloatElement|decodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Decoder // com.eignex.kencode/PackedDecoder.decodeInline|decodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun decodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Decoder // com.eignex.kencode/PackedDecoder.decodeInlineElement|decodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeInt(): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeInt|decodeInt(){}[0] + final fun decodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeIntElement|decodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeLong(): kotlin/Long // com.eignex.kencode/PackedDecoder.decodeLong|decodeLong(){}[0] + final fun decodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Long // com.eignex.kencode/PackedDecoder.decodeLongElement|decodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeNotNullMark(): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeNotNullMark|decodeNotNullMark(){}[0] + final fun decodeNull(): kotlin/Nothing? // com.eignex.kencode/PackedDecoder.decodeNull|decodeNull(){}[0] + final fun decodeSequentially(): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeSequentially|decodeSequentially(){}[0] + final fun decodeShort(): kotlin/Short // com.eignex.kencode/PackedDecoder.decodeShort|decodeShort(){}[0] + final fun decodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Short // com.eignex.kencode/PackedDecoder.decodeShortElement|decodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun decodeString(): kotlin/String // com.eignex.kencode/PackedDecoder.decodeString|decodeString(){}[0] + final fun decodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // com.eignex.kencode/PackedDecoder.decodeStringElement|decodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // com.eignex.kencode/PackedDecoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0] +} + +final class com.eignex.kencode/PackedEncoder : kotlinx.serialization.encoding/CompositeEncoder, kotlinx.serialization.encoding/Encoder { // com.eignex.kencode/PackedEncoder|null[0] + final val serializersModule // com.eignex.kencode/PackedEncoder.serializersModule|{}serializersModule[0] + final fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedEncoder.serializersModule.|(){}[0] + + final fun <#A1: kotlin/Any> encodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1?) // com.eignex.kencode/PackedEncoder.encodeNullableSerializableElement|encodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0?){0§}[0] + final fun <#A1: kotlin/Any?> encodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1) // com.eignex.kencode/PackedEncoder.encodeSerializableElement|encodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0] + final fun <#A1: kotlin/Any?> encodeSerializableValue(kotlinx.serialization/SerializationStrategy<#A1>, #A1) // com.eignex.kencode/PackedEncoder.encodeSerializableValue|encodeSerializableValue(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0] + final fun beginCollection(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/CompositeEncoder // com.eignex.kencode/PackedEncoder.beginCollection|beginCollection(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeEncoder // com.eignex.kencode/PackedEncoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun encodeBoolean(kotlin/Boolean) // com.eignex.kencode/PackedEncoder.encodeBoolean|encodeBoolean(kotlin.Boolean){}[0] + final fun encodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Boolean) // com.eignex.kencode/PackedEncoder.encodeBooleanElement|encodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Boolean){}[0] + final fun encodeByte(kotlin/Byte) // com.eignex.kencode/PackedEncoder.encodeByte|encodeByte(kotlin.Byte){}[0] + final fun encodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Byte) // com.eignex.kencode/PackedEncoder.encodeByteElement|encodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Byte){}[0] + final fun encodeChar(kotlin/Char) // com.eignex.kencode/PackedEncoder.encodeChar|encodeChar(kotlin.Char){}[0] + final fun encodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Char) // com.eignex.kencode/PackedEncoder.encodeCharElement|encodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Char){}[0] + final fun encodeDouble(kotlin/Double) // com.eignex.kencode/PackedEncoder.encodeDouble|encodeDouble(kotlin.Double){}[0] + final fun encodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Double) // com.eignex.kencode/PackedEncoder.encodeDoubleElement|encodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Double){}[0] + final fun encodeEnum(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int) // com.eignex.kencode/PackedEncoder.encodeEnum|encodeEnum(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun encodeFloat(kotlin/Float) // com.eignex.kencode/PackedEncoder.encodeFloat|encodeFloat(kotlin.Float){}[0] + final fun encodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Float) // com.eignex.kencode/PackedEncoder.encodeFloatElement|encodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Float){}[0] + final fun encodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Encoder // com.eignex.kencode/PackedEncoder.encodeInline|encodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0] + final fun encodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Encoder // com.eignex.kencode/PackedEncoder.encodeInlineElement|encodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0] + final fun encodeInt(kotlin/Int) // com.eignex.kencode/PackedEncoder.encodeInt|encodeInt(kotlin.Int){}[0] + final fun encodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Int) // com.eignex.kencode/PackedEncoder.encodeIntElement|encodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Int){}[0] + final fun encodeLong(kotlin/Long) // com.eignex.kencode/PackedEncoder.encodeLong|encodeLong(kotlin.Long){}[0] + final fun encodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Long) // com.eignex.kencode/PackedEncoder.encodeLongElement|encodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Long){}[0] + final fun encodeNotNullMark() // com.eignex.kencode/PackedEncoder.encodeNotNullMark|encodeNotNullMark(){}[0] + final fun encodeNull() // com.eignex.kencode/PackedEncoder.encodeNull|encodeNull(){}[0] + final fun encodeShort(kotlin/Short) // com.eignex.kencode/PackedEncoder.encodeShort|encodeShort(kotlin.Short){}[0] + final fun encodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Short) // com.eignex.kencode/PackedEncoder.encodeShortElement|encodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Short){}[0] + final fun encodeString(kotlin/String) // com.eignex.kencode/PackedEncoder.encodeString|encodeString(kotlin.String){}[0] + final fun encodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/String) // com.eignex.kencode/PackedEncoder.encodeStringElement|encodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.String){}[0] + final fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // com.eignex.kencode/PackedEncoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0] +} + +final class com.eignex.kencode/PackedFormatBuilder { // com.eignex.kencode/PackedFormatBuilder|null[0] + constructor () // com.eignex.kencode/PackedFormatBuilder.|(){}[0] + + final var defaultEncoding // com.eignex.kencode/PackedFormatBuilder.defaultEncoding|{}defaultEncoding[0] + final fun (): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedFormatBuilder.defaultEncoding.|(){}[0] + final fun (com.eignex.kencode/IntPacking) // com.eignex.kencode/PackedFormatBuilder.defaultEncoding.|(com.eignex.kencode.IntPacking){}[0] + final var serializersModule // com.eignex.kencode/PackedFormatBuilder.serializersModule|{}serializersModule[0] + final fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedFormatBuilder.serializersModule.|(){}[0] + final fun (kotlinx.serialization.modules/SerializersModule) // com.eignex.kencode/PackedFormatBuilder.serializersModule.|(kotlinx.serialization.modules.SerializersModule){}[0] +} + +final class com.eignex.kencode/UnicodeRangeAlphabet : com.eignex.kencode/Alphabet { // com.eignex.kencode/UnicodeRangeAlphabet|null[0] + constructor (kotlin/Int = ..., kotlin/Int = ...) // com.eignex.kencode/UnicodeRangeAlphabet.|(kotlin.Int;kotlin.Int){}[0] + + final val size // com.eignex.kencode/UnicodeRangeAlphabet.size|{}size[0] + final fun (): kotlin/Int // com.eignex.kencode/UnicodeRangeAlphabet.size.|(){}[0] + + final fun get(kotlin/Int): kotlin/Char // com.eignex.kencode/UnicodeRangeAlphabet.get|get(kotlin.Int){}[0] + final fun indexOf(kotlin/Char): kotlin/Int // com.eignex.kencode/UnicodeRangeAlphabet.indexOf|indexOf(kotlin.Char){}[0] +} + +open class com.eignex.kencode/Base64 : com.eignex.kencode/ByteEncoding { // com.eignex.kencode/Base64|null[0] + constructor (kotlin/CharArray) // com.eignex.kencode/Base64.|(kotlin.CharArray){}[0] + + final val alphabet // com.eignex.kencode/Base64.alphabet|{}alphabet[0] + final fun (): kotlin/CharArray // com.eignex.kencode/Base64.alphabet.|(){}[0] + + open fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/Base64.decode|decode(kotlin.CharSequence){}[0] + open fun encode(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/String // com.eignex.kencode/Base64.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + + final object Default : com.eignex.kencode/Base64 // com.eignex.kencode/Base64.Default|null[0] +} + +open class com.eignex.kencode/Base85 : com.eignex.kencode/ByteEncoding { // com.eignex.kencode/Base85|null[0] + constructor (kotlin/CharArray) // com.eignex.kencode/Base85.|(kotlin.CharArray){}[0] + + open fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/Base85.decode|decode(kotlin.CharSequence){}[0] + open fun encode(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/String // com.eignex.kencode/Base85.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + + final object Default : com.eignex.kencode/Base85 // com.eignex.kencode/Base85.Default|null[0] +} + +open class com.eignex.kencode/BaseRadix : com.eignex.kencode/ByteEncoding { // com.eignex.kencode/BaseRadix|null[0] + constructor (com.eignex.kencode/Alphabet, kotlin/Int = ...) // com.eignex.kencode/BaseRadix.|(com.eignex.kencode.Alphabet;kotlin.Int){}[0] + constructor (kotlin/String, kotlin/Int = ...) // com.eignex.kencode/BaseRadix.|(kotlin.String;kotlin.Int){}[0] + + final val blockSize // com.eignex.kencode/BaseRadix.blockSize|{}blockSize[0] + final fun (): kotlin/Int // com.eignex.kencode/BaseRadix.blockSize.|(){}[0] + + open fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/BaseRadix.decode|decode(kotlin.CharSequence){}[0] + open fun encode(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/String // com.eignex.kencode/BaseRadix.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] +} + +open class com.eignex.kencode/Crc16 : com.eignex.kencode/Crc { // com.eignex.kencode/Crc16|null[0] + constructor (kotlin/Int = ..., kotlin/Int = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Int = ...) // com.eignex.kencode/Crc16.|(kotlin.Int;kotlin.Int;kotlin.Boolean;kotlin.Boolean;kotlin.Int){}[0] + + final object Default : com.eignex.kencode/Crc16 // com.eignex.kencode/Crc16.Default|null[0] +} + +open class com.eignex.kencode/Crc32 : com.eignex.kencode/Crc { // com.eignex.kencode/Crc32|null[0] + constructor (kotlin/Int = ..., kotlin/Int = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Int = ...) // com.eignex.kencode/Crc32.|(kotlin.Int;kotlin.Int;kotlin.Boolean;kotlin.Boolean;kotlin.Int){}[0] + + final object Default : com.eignex.kencode/Crc32 // com.eignex.kencode/Crc32.Default|null[0] +} + +open class com.eignex.kencode/Crc8 : com.eignex.kencode/Crc { // com.eignex.kencode/Crc8|null[0] + constructor (kotlin/Int = ..., kotlin/Int = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Int = ...) // com.eignex.kencode/Crc8.|(kotlin.Int;kotlin.Int;kotlin.Boolean;kotlin.Boolean;kotlin.Int){}[0] + + final object Default : com.eignex.kencode/Crc8 // com.eignex.kencode/Crc8.Default|null[0] +} + +open class com.eignex.kencode/EncodedFormat : kotlinx.serialization/StringFormat { // com.eignex.kencode/EncodedFormat|null[0] + constructor (com.eignex.kencode/ByteEncoding = ..., com.eignex.kencode/PayloadTransform? = ..., kotlinx.serialization/BinaryFormat = ...) // com.eignex.kencode/EncodedFormat.|(com.eignex.kencode.ByteEncoding;com.eignex.kencode.PayloadTransform?;kotlinx.serialization.BinaryFormat){}[0] + constructor (com.eignex.kencode/EncodedConfiguration) // com.eignex.kencode/EncodedFormat.|(com.eignex.kencode.EncodedConfiguration){}[0] + + final val configuration // com.eignex.kencode/EncodedFormat.configuration|{}configuration[0] + final fun (): com.eignex.kencode/EncodedConfiguration // com.eignex.kencode/EncodedFormat.configuration.|(){}[0] + open val serializersModule // com.eignex.kencode/EncodedFormat.serializersModule|{}serializersModule[0] + open fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/EncodedFormat.serializersModule.|(){}[0] + + open fun <#A1: kotlin/Any?> decodeFromString(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/String): #A1 // com.eignex.kencode/EncodedFormat.decodeFromString|decodeFromString(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.String){0§}[0] + open fun <#A1: kotlin/Any?> encodeToString(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/String // com.eignex.kencode/EncodedFormat.encodeToString|encodeToString(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0] + + final object Default : com.eignex.kencode/EncodedFormat // com.eignex.kencode/EncodedFormat.Default|null[0] +} + +open class com.eignex.kencode/PackedFormat : kotlinx.serialization/BinaryFormat { // com.eignex.kencode/PackedFormat|null[0] + constructor (com.eignex.kencode/PackedConfiguration = ..., kotlinx.serialization.modules/SerializersModule = ...) // com.eignex.kencode/PackedFormat.|(com.eignex.kencode.PackedConfiguration;kotlinx.serialization.modules.SerializersModule){}[0] + + final val configuration // com.eignex.kencode/PackedFormat.configuration|{}configuration[0] + final fun (): com.eignex.kencode/PackedConfiguration // com.eignex.kencode/PackedFormat.configuration.|(){}[0] + open val serializersModule // com.eignex.kencode/PackedFormat.serializersModule|{}serializersModule[0] + open fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedFormat.serializersModule.|(){}[0] + + open fun <#A1: kotlin/Any?> decodeFromByteArray(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/ByteArray): #A1 // com.eignex.kencode/PackedFormat.decodeFromByteArray|decodeFromByteArray(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.ByteArray){0§}[0] + open fun <#A1: kotlin/Any?> encodeToByteArray(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/ByteArray // com.eignex.kencode/PackedFormat.encodeToByteArray|encodeToByteArray(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0] + + final object Default : com.eignex.kencode/PackedFormat // com.eignex.kencode/PackedFormat.Default|null[0] +} + +final object com.eignex.kencode/Base36 : com.eignex.kencode/BaseRadix // com.eignex.kencode/Base36|null[0] + +final object com.eignex.kencode/Base62 : com.eignex.kencode/BaseRadix // com.eignex.kencode/Base62|null[0] + +final object com.eignex.kencode/Base64Url : com.eignex.kencode/Base64 // com.eignex.kencode/Base64Url|null[0] + +final object com.eignex.kencode/CompactZeros : com.eignex.kencode/PayloadTransform { // com.eignex.kencode/CompactZeros|null[0] + final fun decode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/CompactZeros.decode|decode(kotlin.ByteArray){}[0] + final fun encode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/CompactZeros.encode|encode(kotlin.ByteArray){}[0] +} + +final const val com.eignex.kencode/ASCII85 // com.eignex.kencode/ASCII85|{}ASCII85[0] + final fun (): kotlin/String // com.eignex.kencode/ASCII85.|(){}[0] +final const val com.eignex.kencode/BASE_62 // com.eignex.kencode/BASE_62|{}BASE_62[0] + final fun (): kotlin/String // com.eignex.kencode/BASE_62.|(){}[0] +final const val com.eignex.kencode/BASE_64 // com.eignex.kencode/BASE_64|{}BASE_64[0] + final fun (): kotlin/String // com.eignex.kencode/BASE_64.|(){}[0] +final const val com.eignex.kencode/BASE_64_URL // com.eignex.kencode/BASE_64_URL|{}BASE_64_URL[0] + final fun (): kotlin/String // com.eignex.kencode/BASE_64_URL.|(){}[0] + +final fun (com.eignex.kencode/Checksum).com.eignex.kencode/asTransform(): com.eignex.kencode/PayloadTransform // com.eignex.kencode/asTransform|asTransform@com.eignex.kencode.Checksum(){}[0] +final fun (com.eignex.kencode/PayloadTransform).com.eignex.kencode/then(com.eignex.kencode/PayloadTransform): com.eignex.kencode/PayloadTransform // com.eignex.kencode/then|then@com.eignex.kencode.PayloadTransform(com.eignex.kencode.PayloadTransform){}[0] +final fun com.eignex.kencode/EncodedFormat(com.eignex.kencode/EncodedFormat = ..., kotlin/Function1): com.eignex.kencode/EncodedFormat // com.eignex.kencode/EncodedFormat|EncodedFormat(com.eignex.kencode.EncodedFormat;kotlin.Function1){}[0] +final fun com.eignex.kencode/PackedFormat(com.eignex.kencode/PackedFormat = ..., kotlin/Function1): com.eignex.kencode/PackedFormat // com.eignex.kencode/PackedFormat|PackedFormat(com.eignex.kencode.PackedFormat;kotlin.Function1){}[0] diff --git a/api/kencode.klib.api b/api/kencode.klib.api index da1e16c..280975b 100644 --- a/api/kencode.klib.api +++ b/api/kencode.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, mingwX64, wasmJs, wasmWasi] +// Targets: [linuxX64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true diff --git a/build.gradle.kts b/build.gradle.kts index 55fac69..e31bad9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl plugins { id("com.eignex.kmp") + id("com.eignex.publish.github") kotlin("plugin.serialization") } @@ -19,15 +20,34 @@ eignexPublish { githubRepo.set("Eignex/kencode") } -@OptIn(ExperimentalWasmDsl::class) +// Run the real JVM artifact on both the supported floor and the current LTS. +eignexBuild { + packagedTestJdks.set(listOf(17, 25)) +} + +tasks.withType().configureEach { + // Do not reuse results produced with the old aggregate-only failure policy. + inputs.property("kotlin.tests.individualTaskReports", providers.gradleProperty("kotlin.tests.individualTaskReports")) +} + +// Routine checks cover JVM + Linux x64; release validation opts into every target. +val fullTargets = providers.gradleProperty("targets.full").isPresent + +@OptIn(ExperimentalWasmDsl::class, org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class) kotlin { + abiValidation { + referenceDumpDir.set(layout.projectDirectory.dir(if (fullTargets) "api-full" else "api")) + } jvm() - js { browser(); nodejs() } - wasmJs { browser(); nodejs() } - wasmWasi { nodejs() } - linuxX64(); linuxArm64() - macosArm64(); mingwX64() - iosX64(); iosArm64(); iosSimulatorArm64() + linuxX64() + if (fullTargets) { + js { browser(); nodejs() } + wasmJs { browser(); nodejs() } + wasmWasi { nodejs() } + linuxArm64() + macosArm64(); mingwX64() + iosX64(); iosArm64(); iosSimulatorArm64() + } sourceSets { commonMain.dependencies { @@ -58,3 +78,8 @@ dokka { } } } + +// Select an installed simulator in CI rather than relying on a particular iPhone model. +tasks.withType().configureEach { + providers.gradleProperty("iosSimulatorDevice").orNull?.let { device.set(it) } +} diff --git a/consumer/build.gradle.kts b/consumer/build.gradle.kts new file mode 100644 index 0000000..61213ba --- /dev/null +++ b/consumer/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { application } + +val pomOnly = providers.gradleProperty("consumer.pomOnly").getOrElse("false").toBoolean() +val library = providers.gradleProperty("consumer.library").get() +val module = if (pomOnly) "$library-jvm" else library +val version = providers.gradleProperty("consumer.version").get() + +dependencies { + // No KBuild plugin, platform, or Kotlin dependency declaration: published metadata must suffice. + implementation("com.eignex:$module:$version") { + // CI reuses a private staging version; always inspect its current local metadata/artifact. + isChanging = true + } +} + +java { toolchain.languageVersion.set(JavaLanguageVersion.of(17)) } +application { mainClass.set("Consumer") } + +configurations.configureEach { + resolutionStrategy.cacheChangingModulesFor(0, "seconds") +} diff --git a/consumer/gradle.properties b/consumer/gradle.properties new file mode 100644 index 0000000..c0d290c --- /dev/null +++ b/consumer/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.configuration-cache=true +org.gradle.caching=true +org.gradle.warning.mode=fail +consumer.library=kencode diff --git a/consumer/settings.gradle.kts b/consumer/settings.gradle.kts new file mode 100644 index 0000000..f0f5104 --- /dev/null +++ b/consumer/settings.gradle.kts @@ -0,0 +1,29 @@ +rootProject.name = "library-consumer" + +plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } + +dependencyResolutionManagement { + repositories { + exclusiveContent { + forRepository { + val githubRepository = providers.gradleProperty("consumer.githubRepository") + if (githubRepository.isPresent) { + ivy { + url = uri(githubRepository.get()) + patternLayout { artifact("[artifact]-[revision](-[classifier]).[ext]") } + metadataSources { gradleMetadata() } + } + } else { + maven { + url = uri(providers.gradleProperty("consumer.repository").get()) + if (providers.gradleProperty("consumer.pomOnly").getOrElse("false").toBoolean()) { + metadataSources { mavenPom(); artifact(); ignoreGradleMetadataRedirection() } + } + } + } + } + filter { includeGroup("com.eignex") } + } + mavenCentral() + } +} diff --git a/consumer/src/main/java/Consumer.java b/consumer/src/main/java/Consumer.java new file mode 100644 index 0000000..40f9961 --- /dev/null +++ b/consumer/src/main/java/Consumer.java @@ -0,0 +1,24 @@ +import com.eignex.kencode.Base62; +import com.eignex.kencode.Base64; +import com.eignex.kencode.Base64Url; +import com.eignex.kencode.Base85; +import com.eignex.kencode.ByteEncoding; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public final class Consumer { + public static void main(String[] args) { + var bytes = "Published encoding 0123456789".getBytes(StandardCharsets.UTF_8); + ByteEncoding[] codecs = {Base64.Default, Base64Url.INSTANCE, Base85.Default, Base62.INSTANCE}; + for (var codec : codecs) { + var encoded = codec.encode(bytes, 0, bytes.length); + if (!Arrays.equals(bytes, codec.decode(encoded))) { + throw new AssertionError("Published encoding failed to round trip"); + } + } + if (!Base64.Default.encode(bytes, 0, bytes.length).equals(java.util.Base64.getEncoder().encodeToString(bytes))) { + throw new AssertionError("Published Base64 differs from JDK Base64"); + } + System.out.println("Published Java consumer passed"); + } +} diff --git a/gradle.properties b/gradle.properties index a675318..8990cef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,3 +4,4 @@ kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g org.gradle.workers.max=4 kotlin.native.ignoreDisabledTargets=true +kotlin.tests.individualTaskReports=true diff --git a/settings.gradle.kts b/settings.gradle.kts index 9824f50..1ea56e6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { mavenCentral() } resolutionStrategy.eachPlugin { - if (requested.id.id in setOf("com.eignex.kmp", "org.jetbrains.kotlin.plugin.serialization")) { + if (requested.id.id in setOf("com.eignex.kmp", "com.eignex.publish.github", "org.jetbrains.kotlin.plugin.serialization")) { useModule("com.eignex:kbuild:$kbuildVersion") } }