From 76db31854ef551318ab3316b043207d8930ae2a8 Mon Sep 17 00:00:00 2001 From: Titouan Bion Date: Tue, 14 Oct 2025 21:57:54 +0200 Subject: [PATCH] [fleet] [FL-33222] Move buildtool libraries to JPS model for re-use in Bazel rules / Haven CLI GitOrigin-RevId: 908e04e94f1bb3f5c65c3509099c0708511c7adc --- .../libraries/fleet_aws_sdk_kotlin_s3_jvm.xml | 213 +++ .idea/libraries/fleet_clikt.xml | 45 + .idea/libraries/fleet_clikt_core.xml | 18 + .../libraries/fleet_github_luben_zstd_jni.xml | 20 + ...et_io_github_pdvrieze_xmlutil_core_jvm.xml | 20 + ...hub_pdvrieze_xmlutil_serialization_jvm.xml | 20 + .../fleet_io_ktor_client_logging.xml | 18 + .idea/libraries/fleet_io_ktor_client_mock.xml | 20 + ...ains_intellij_idea_community_build_zip.xml | 45 + ...ns_intellij_platform_util_zip_squashed.xml | 18 + .idea/libraries/fleet_tukaani_xz.xml | 20 + .idea/modules.xml | 9 + build/bazel-generated-file-list.txt | 9 + fleet/.gitignore | 2 +- fleet/build/authentication/BUILD.bazel | 31 + .../fleet.build.authentication.iml | 39 + .../gradlebuild/build.gradle.kts | 71 + .../buildtool/authentication/AuthUtils.kt | 61 + fleet/build/cli/BUILD.bazel | 44 + fleet/build/cli/fleet.build.cli.iml | 32 + fleet/build/cli/gradlebuild/build.gradle.kts | 86 ++ .../srcJvmMain/fleet/buildtool/cli/main.kt | 31 + fleet/build/codecache/BUILD.bazel | 34 + .../build/codecache/fleet.build.codecache.iml | 36 + .../codecache/gradlebuild/build.gradle.kts | 69 + .../buildtool/codecache/HashJarsUtils.kt | 31 + .../codecache/ModuleDescriptorSerializer.kt | 370 ++++++ .../fleet/buildtool/codecache/ModulePacker.kt | 393 ++++++ .../fleet/buildtool/codecache/PluginPart.kt | 44 + fleet/build/fleet.bzl | 3 + fleet/build/fs/BUILD.bazel | 67 + fleet/build/fs/fleet.build.fs.iml | 38 + fleet/build/fs/gradlebuild/build.gradle.kts | 76 ++ .../build/fs/resourcesJvmTest/.gitattributes | 3 + .../archive/empty_folder.tar.gz | Bin 0 -> 358 bytes .../resourcesJvmTest/archive/empty_folder.zip | Bin 0 -> 440 bytes .../fs/resourcesJvmTest/archive/mixed.tar.gz | Bin 0 -> 851 bytes .../fs/resourcesJvmTest/archive/mixed.zip | Bin 0 -> 2427 bytes .../resourcesJvmTest/archive/readonly.tar.gz | Bin 0 -> 454 bytes .../fs/resourcesJvmTest/archive/readonly.zip | Bin 0 -> 485 bytes .../archive/single_symlink.tar.gz | Bin 0 -> 1048 bytes .../archive/single_symlink.zip | Bin 0 -> 895 bytes .../fs/resourcesJvmTest/example-abi-cr.txt | 1 + .../fs/resourcesJvmTest/example-abi-crlf.txt | 514 ++++++++ .../fs/resourcesJvmTest/example-abi-lf.txt | 514 ++++++++ .../fleet/buildtool/fs/ArchiveUtils.kt | 533 ++++++++ .../fs/srcJvmMain/fleet/buildtool/fs/Utils.kt | 65 + .../fleet/buildtool/fs/ArchiveUtilsTest.kt | 1162 +++++++++++++++++ .../fleet/buildtool/fs/UtilsTest.kt | 48 + fleet/build/http/BUILD.bazel | 69 + fleet/build/http/fleet.build.http.iml | 37 + fleet/build/http/gradlebuild/build.gradle.kts | 76 ++ .../fleet/buildtool/http/HttpClient.kt | 177 +++ .../fleet/buildtool/http/HttpClientTests.kt | 183 +++ fleet/build/license/BUILD.bazel | 28 + fleet/build/license/fleet.build.license.iml | 36 + .../license/gradlebuild/build.gradle.kts | 59 + .../.pseudoCommonKotlinSourceSet | 0 .../fleet/buildtool/license/LicenseUtils.kt | 18 + fleet/build/platform/BUILD.bazel | 29 + fleet/build/platform/fleet.build.platform.iml | 37 + .../platform/gradlebuild/build.gradle.kts | 63 + .../buildtool/platform/DistributionChannel.kt | 32 + .../buildtool/platform/MarketplaceChannel.kt | 15 + .../fleet/buildtool/platform/Platform.kt | 196 +++ .../fleet/buildtool/platform/RustTarget.kt | 28 + .../fleet/buildtool/platform/TeamcityUtils.kt | 5 + fleet/build/rules/fleet_module.bzl | 31 + fleet/build/s3/BUILD.bazel | 30 + fleet/build/s3/fleet.build.s3.iml | 32 + fleet/build/s3/gradlebuild/build.gradle.kts | 65 + .../srcJvmMain/fleet/buildtool/s3/S3Keys.kt | 62 + .../srcJvmMain/fleet/buildtool/s3/S3Utils.kt | 20 + fleet/build/sign/BUILD.bazel | 27 + fleet/build/sign/fleet.build.sign.iml | 29 + fleet/build/sign/gradlebuild/build.gradle.kts | 56 + .../fleet/buildtool/sign/FleetSigner.kt | 11 + .../fleet/buildtool/sign/MockSigner.kt | 23 + lib/BUILD.bazel | 376 +++++- lib/MODULE.bazel | 744 ++++++++++- 80 files changed, 7390 insertions(+), 77 deletions(-) create mode 100644 .idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml create mode 100644 .idea/libraries/fleet_clikt.xml create mode 100644 .idea/libraries/fleet_clikt_core.xml create mode 100644 .idea/libraries/fleet_github_luben_zstd_jni.xml create mode 100644 .idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml create mode 100644 .idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml create mode 100644 .idea/libraries/fleet_io_ktor_client_logging.xml create mode 100644 .idea/libraries/fleet_io_ktor_client_mock.xml create mode 100644 .idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml create mode 100644 .idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml create mode 100644 .idea/libraries/fleet_tukaani_xz.xml create mode 100644 fleet/build/authentication/BUILD.bazel create mode 100644 fleet/build/authentication/fleet.build.authentication.iml create mode 100644 fleet/build/authentication/gradlebuild/build.gradle.kts create mode 100644 fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt create mode 100644 fleet/build/cli/BUILD.bazel create mode 100644 fleet/build/cli/fleet.build.cli.iml create mode 100644 fleet/build/cli/gradlebuild/build.gradle.kts create mode 100644 fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt create mode 100644 fleet/build/codecache/BUILD.bazel create mode 100644 fleet/build/codecache/fleet.build.codecache.iml create mode 100644 fleet/build/codecache/gradlebuild/build.gradle.kts create mode 100644 fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt create mode 100644 fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt create mode 100644 fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt create mode 100644 fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt create mode 100644 fleet/build/fleet.bzl create mode 100644 fleet/build/fs/BUILD.bazel create mode 100644 fleet/build/fs/fleet.build.fs.iml create mode 100644 fleet/build/fs/gradlebuild/build.gradle.kts create mode 100644 fleet/build/fs/resourcesJvmTest/.gitattributes create mode 100644 fleet/build/fs/resourcesJvmTest/archive/empty_folder.tar.gz create mode 100644 fleet/build/fs/resourcesJvmTest/archive/empty_folder.zip create mode 100644 fleet/build/fs/resourcesJvmTest/archive/mixed.tar.gz create mode 100644 fleet/build/fs/resourcesJvmTest/archive/mixed.zip create mode 100644 fleet/build/fs/resourcesJvmTest/archive/readonly.tar.gz create mode 100644 fleet/build/fs/resourcesJvmTest/archive/readonly.zip create mode 100644 fleet/build/fs/resourcesJvmTest/archive/single_symlink.tar.gz create mode 100644 fleet/build/fs/resourcesJvmTest/archive/single_symlink.zip create mode 100644 fleet/build/fs/resourcesJvmTest/example-abi-cr.txt create mode 100644 fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt create mode 100644 fleet/build/fs/resourcesJvmTest/example-abi-lf.txt create mode 100644 fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt create mode 100644 fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt create mode 100644 fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt create mode 100644 fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt create mode 100644 fleet/build/http/BUILD.bazel create mode 100644 fleet/build/http/fleet.build.http.iml create mode 100644 fleet/build/http/gradlebuild/build.gradle.kts create mode 100644 fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt create mode 100644 fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt create mode 100644 fleet/build/license/BUILD.bazel create mode 100644 fleet/build/license/fleet.build.license.iml create mode 100644 fleet/build/license/gradlebuild/build.gradle.kts create mode 100644 fleet/build/license/srcCommonMain/.pseudoCommonKotlinSourceSet create mode 100644 fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt create mode 100644 fleet/build/platform/BUILD.bazel create mode 100644 fleet/build/platform/fleet.build.platform.iml create mode 100644 fleet/build/platform/gradlebuild/build.gradle.kts create mode 100644 fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt create mode 100644 fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt create mode 100644 fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt create mode 100644 fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt create mode 100644 fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt create mode 100644 fleet/build/rules/fleet_module.bzl create mode 100644 fleet/build/s3/BUILD.bazel create mode 100644 fleet/build/s3/fleet.build.s3.iml create mode 100644 fleet/build/s3/gradlebuild/build.gradle.kts create mode 100644 fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt create mode 100644 fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt create mode 100644 fleet/build/sign/BUILD.bazel create mode 100644 fleet/build/sign/fleet.build.sign.iml create mode 100644 fleet/build/sign/gradlebuild/build.gradle.kts create mode 100644 fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt create mode 100644 fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt diff --git a/.idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml b/.idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml new file mode 100644 index 000000000000..b36b258cd706 --- /dev/null +++ b/.idea/libraries/fleet_aws_sdk_kotlin_s3_jvm.xml @@ -0,0 +1,213 @@ + + + + + + 3d6cd0bf48c65e965903fa4720d713badeeaa5433bd7b278075c484e3a5709a8 + + + 877ffeaa6fc96192da7383b3c3e16717cb52504f488b80937d796eb875e5565a + + + 2f12ed8fbd53e91cb3b2a163b5d06ae2c39f538bd74f23a678889f5be02e8675 + + + 198b9b1ec6c33da8c460e6a2e0859d1308f1fcb3f7d4138100f7dd75207edd80 + + + 633ccbae483c99b27e311a6a70157cfc0dfa19f8eb3ba263198f40f87266c09c + + + e738ebef7be523c4e1940f5e5b2390ba853d4333f547038b2b31fb3964e99933 + + + d3f30ef5e3b11b358f28f42422226585b72e4a11bf13b1fc3411962dcfde64f3 + + + aa584dcd05862a8bbe7eb7db73959d93ca6b527cc44536b7d10fcda733411d4b + + + 8a2323e7d08ff07d2f869e4bcae834773ce0a0e8704cd7404c9625992a748662 + + + 67543f0736fc422ae927ed0e504b98bc5e269fda0d3500579337cb713da28412 + + + ce382d40f998d8dd11008967c3bf4cd51c47182512b84b5f1d48e48d2a3f9770 + + + 508bde368d4b174402aeeccec68c7e29ad9349a953b1efab75ebeaa6c0a21f0e + + + 01489854c4f9494de6cd5446df8e3c694ad3f6582c5369b9578eb18754f1720c + + + 71fe60ba15b5f9c43f5ee1da0edc13fd98f2a85dbf8c9dcafc666bc1c7e668e7 + + + 6b7584f233e0715f5394edfbfd5536a480113efe99f3d49bfd58d2395072177a + + + 6d7028c6e695b872e0ba91679af4dbbabf8e1bfbd6f7fb52be30bb1e398d595d + + + 30cf05f9f7ec7d6676031ee2b0c8f4b33b107a93a179ca931d57baa983de70a3 + + + dd0616302ce4a3fc1c03326bad37e5cf3509377692b6aca5ea17d581ad582d62 + + + 94cc5ebcb2d819ebb8c628f64ffa352426f23b3b422834bfa8199cf497a1365d + + + 3f0d5d4e67a3097b56b32fa7b26589da9d6bd0968693b395ad1c8f49f60dbdfd + + + 79ed729b7e134609310de7d691b1b80870f467a95bc84f2f6bbf3e43ae83de85 + + + 9664c8d36468c1f575b356f04025ce17417e584a0bb19b640b115b10a8dce72c + + + 31e3811fd85ba7fcba38a4067d63af8c13c1e05ae7062639fd8c954520315f51 + + + bbbf92cbc8df78a8fa20f0b35613e8d41024ee6525b538ebda280917e1d7e812 + + + 4bfb91b4c6373ac2b1d94a085c69935ca1b56b7b27b21c5228c5bc5da6f7e2dc + + + 506a1d3aba9f99b55570c7f8813cd79a44946531f29050d5b938474efdb4cf80 + + + 1452873e0cc9637b7e1a717f2cca0cbccebd00c05aaaf67b8adb06777bf4f461 + + + be3f9bd5ebfb83aa353f407a4389f7b3454a6945f82cea1d41e370aeedae71cd + + + c017ee899656cf5abf22c3793603cb33ae35f0a805b9fa0707f8146a73a14a7f + + + 72c8559a241af92ce122c0df0dc3749d638d616ebd5da3ba3749a95abca3b349 + + + 4343154bb1b0a282d86b627932a465722eeda3cccd6cc4986fc5e5c4013bfef4 + + + 66c0700ca0c27046b0b12e6cf4c4da423800b88c1c72b2709cd5cbc3e0f1eb64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_clikt.xml b/.idea/libraries/fleet_clikt.xml new file mode 100644 index 000000000000..7a14dc5365e1 --- /dev/null +++ b/.idea/libraries/fleet_clikt.xml @@ -0,0 +1,45 @@ + + + + + + 9cd19e04782f461dffeb183d17e576984febb7934932cb406646ac41e428863b + + + 9ed3b976fcccc78da746d49866fa8ebb8f10530a93c544ea0420259a607dd95e + + + 9cf9b46d1f49f2d6cf2635462b29dc59e0c29b1fb2f085b3312888bbe9c7cd31 + + + 59f741adfe62053066782d8b1a45afd06685a4bc64b33277e54876b993ed885c + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_clikt_core.xml b/.idea/libraries/fleet_clikt_core.xml new file mode 100644 index 000000000000..ef92ae111018 --- /dev/null +++ b/.idea/libraries/fleet_clikt_core.xml @@ -0,0 +1,18 @@ + + + + + + 69cba94a2291f74b78c88239cd8b67eaf3f08e0dad297749abe6477b3e9b81b1 + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_github_luben_zstd_jni.xml b/.idea/libraries/fleet_github_luben_zstd_jni.xml new file mode 100644 index 000000000000..ebdbe2edc3cd --- /dev/null +++ b/.idea/libraries/fleet_github_luben_zstd_jni.xml @@ -0,0 +1,20 @@ + + + + + + d75b2ced6059f81ad23e021c554259b906b6c4f2991cb772409827569ead4c1a + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml new file mode 100644 index 000000000000..b9c996f4d349 --- /dev/null +++ b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_core_jvm.xml @@ -0,0 +1,20 @@ + + + + + + 91527d86fea04bd6183d140a09f10dab2dea727af12d27d9165ee342ea3d0ede + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml new file mode 100644 index 000000000000..c2134c9bc9d8 --- /dev/null +++ b/.idea/libraries/fleet_io_github_pdvrieze_xmlutil_serialization_jvm.xml @@ -0,0 +1,20 @@ + + + + + + 9ce273dcb860b92a5bf2ec36ad1e2a11b41aed89c6c984ca73e87eff5eda1bd0 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_io_ktor_client_logging.xml b/.idea/libraries/fleet_io_ktor_client_logging.xml new file mode 100644 index 000000000000..9957ab2c2a26 --- /dev/null +++ b/.idea/libraries/fleet_io_ktor_client_logging.xml @@ -0,0 +1,18 @@ + + + + + + ecabf013b137f1a69c39cffeae1ba9e365659357b21688f1876ee6cf50fd00b9 + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_io_ktor_client_mock.xml b/.idea/libraries/fleet_io_ktor_client_mock.xml new file mode 100644 index 000000000000..d4369aba6af1 --- /dev/null +++ b/.idea/libraries/fleet_io_ktor_client_mock.xml @@ -0,0 +1,20 @@ + + + + + + 2ae0d57bb9fceb8ee2aeda6536acc6f3e1de355984b4c9da720566b85f1525e8 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml b/.idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml new file mode 100644 index 000000000000..69a329d23d7c --- /dev/null +++ b/.idea/libraries/fleet_jetbrains_intellij_idea_community_build_zip.xml @@ -0,0 +1,45 @@ + + + + + + c86d2bfa23d5d23f916d3574591a166b3df259bea6c23d39abea597b34ba022f + + + 74d953cec00048b892112f9602fc7b1113ea55337dc1ede32e449efcb2b231c7 + + + cc65afe17daf2f9380d3c64460f5b10ec279f98b9461fbf0ccf62b3cb3ee6c37 + + + 5a0d7557d494732a6165e4188eb11f10faca196350cad04d9950abd83e6dd148 + + + 0181873ba85684d119134f5598432f5b63109a03adc455767a1500b2145df8a2 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml b/.idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml new file mode 100644 index 000000000000..f71553540f74 --- /dev/null +++ b/.idea/libraries/fleet_jetbrains_intellij_platform_util_zip_squashed.xml @@ -0,0 +1,18 @@ + + + + + + 7df07e447c1de46592dfe4b20345b61ff57c5f3b796b8e946086559b260f7690 + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/fleet_tukaani_xz.xml b/.idea/libraries/fleet_tukaani_xz.xml new file mode 100644 index 000000000000..4e2bb6af2d60 --- /dev/null +++ b/.idea/libraries/fleet_tukaani_xz.xml @@ -0,0 +1,20 @@ + + + + + + 211b306cfc44f8f96df3a0a3ddaf75ba8c5289eed77d60d72f889bb855f535e5 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 724bf9ea248b..a811ba430e6b 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -4,6 +4,15 @@ + + + + + + + + + diff --git a/build/bazel-generated-file-list.txt b/build/bazel-generated-file-list.txt index 0f00dc93ea40..590ac37c3906 100644 --- a/build/bazel-generated-file-list.txt +++ b/build/bazel-generated-file-list.txt @@ -221,6 +221,15 @@ commandInterface community-resources fleet/andel fleet/bifurcan +fleet/build/authentication +fleet/build/cli +fleet/build/codecache +fleet/build/fs +fleet/build/http +fleet/build/license +fleet/build/platform +fleet/build/s3 +fleet/build/sign fleet/bundles fleet/codecache fleet/codecache/test diff --git a/fleet/.gitignore b/fleet/.gitignore index baf5c70f19e3..dd9a60b31472 100644 --- a/fleet/.gitignore +++ b/fleet/.gitignore @@ -1 +1 @@ -/**/build \ No newline at end of file +/**/gradlebuild/build \ No newline at end of file diff --git a/fleet/build/authentication/BUILD.bazel b/fleet/build/authentication/BUILD.bazel new file mode 100644 index 000000000000..74163ca9d347 --- /dev/null +++ b/fleet/build/authentication/BUILD.bazel @@ -0,0 +1,31 @@ +### auto-generated section `build fleet.build.authentication` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_authentication", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "authentication", + module_name = "fleet.build.authentication", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_authentication", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-serialization-core", + "@lib//:kotlinx-serialization-json", + "@lib//:slf4j-api", + "@lib//:fleet-io-github-pdvrieze-xmlutil-serialization-jvm", + "@lib//:fleet-io-github-pdvrieze-xmlutil-core-jvm", + ] +) +### auto-generated section `build fleet.build.authentication` end \ No newline at end of file diff --git a/fleet/build/authentication/fleet.build.authentication.iml b/fleet/build/authentication/fleet.build.authentication.iml new file mode 100644 index 000000000000..decd83bfca77 --- /dev/null +++ b/fleet/build/authentication/fleet.build.authentication.iml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/authentication/gradlebuild/build.gradle.kts b/fleet/build/authentication/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..c4d5d2bedc3d --- /dev/null +++ b/fleet/build/authentication/gradlebuild/build.gradle.kts @@ -0,0 +1,71 @@ +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + alias(jps.plugins.kotlin.serialization) + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.authentication" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.core.jvm1739247612.get().let { "${it.group}:kotlinx-serialization-core:${it.version}" }) { + isTransitive = false + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.json.jvm231489733.get().let { "${it.group}:kotlinx-serialization-json:${it.version}" }) { + isTransitive = false + } + implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + exclude(group = "org.slf4j", module = "slf4j-jdk14") + } + } + sourceSets.jvmMain.dependencies { + implementation(jps.io.github.pdvrieze.xmlutil.serialization.jvm796850685.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + } + implementation(jps.io.github.pdvrieze.xmlutil.core.jvm535986330.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + } + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt b/fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt new file mode 100644 index 000000000000..f60a19332cb7 --- /dev/null +++ b/fleet/build/authentication/srcJvmMain/fleet/buildtool/authentication/AuthUtils.kt @@ -0,0 +1,61 @@ +package fleet.buildtool.authentication + +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi +import nl.adaptivity.xmlutil.serialization.XML +import nl.adaptivity.xmlutil.serialization.XmlConfig +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName +import org.slf4j.Logger +import java.nio.file.Path +import kotlin.io.path.exists +import kotlin.io.path.readText + +const val intellijPrivateDependenciesId = "intellij-private-dependencies" + +val defaultSettingsPath by lazy { + Path.of(System.getProperty("user.home")).resolve(".m2/settings.xml") +} + +@OptIn(ExperimentalXmlUtilApi::class) +fun readMavenSettingsServerCredentials( + logger: Logger, + mavenSettingsPath: Path = defaultSettingsPath, +): List { + logger.info("Reading servers credentials from Maven settings '$mavenSettingsPath'") + val xml = XML { + defaultPolicy { + unknownChildHandler = XmlConfig.IGNORING_UNKNOWN_CHILD_HANDLER + } + } + if (!mavenSettingsPath.exists()) { + logger.warn("WARNING: failed to find '$mavenSettingsPath', no repository credentials will be read") + return emptyList() + } + return try { + xml.decodeFromString(MavenSettings.serializer(), mavenSettingsPath.readText()).servers?.servers ?: emptyList() + } + catch (e: Exception) { + logger.warn("WARNING: failed to parse {}", mavenSettingsPath, e) + emptyList() + } +} + +@Serializable +@XmlSerialName( + value = "settings", + namespace = "http://maven.apache.org/SETTINGS/1.0.0", +) +private data class MavenSettings(@XmlElement val servers: MavenServers?) + +@Serializable +@XmlSerialName("servers") +data class MavenServers(@XmlElement val servers: List?) + +@Serializable +@XmlSerialName("server") +data class MavenServerCredentials( + @XmlElement @XmlSerialName("id") val id: String?, + @XmlElement @XmlSerialName("username") val username: String?, + @XmlElement @XmlSerialName("password") val password: String?, +) diff --git a/fleet/build/cli/BUILD.bazel b/fleet/build/cli/BUILD.bazel new file mode 100644 index 000000000000..6bbb656e9515 --- /dev/null +++ b/fleet/build/cli/BUILD.bazel @@ -0,0 +1,44 @@ +load("@rules_java//java:defs.bzl", "java_binary") + + +java_binary( + name = "haven", + main_class = "fleet.buildtool.cli.MainKt", + visibility = ["//visibility:public"], + runtime_deps = [":cli"], +) + +### auto-generated section `build fleet.build.cli` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_cli", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "cli", + module_name = "fleet.build.cli", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_cli", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-io-core", + "@lib//:kotlinx-coroutines-core", + "@lib//:fleet-clikt-core", + "@lib//:fleet-clikt", + ], + exports = [ + "@lib//:fleet-clikt-core", + "@lib//:fleet-clikt", + ] +) +### auto-generated section `build fleet.build.cli` end \ No newline at end of file diff --git a/fleet/build/cli/fleet.build.cli.iml b/fleet/build/cli/fleet.build.cli.iml new file mode 100644 index 000000000000..42ead6491e38 --- /dev/null +++ b/fleet/build/cli/fleet.build.cli.iml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/cli/gradlebuild/build.gradle.kts b/fleet/build/cli/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..c819b58471bf --- /dev/null +++ b/fleet/build/cli/gradlebuild/build.gradle.kts @@ -0,0 +1,86 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.buildtool.cli" + importedFromJps {} + } +} + +kotlin { + jvm { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + binaries { + // Configures a JavaExec task named "runJvm" and a Gradle distribution for the "main" compilation in this target + executable { + mainClass.set("fleet.buildtool.cli.commands.GenerateInitModuleDescriptorCommandKt") + } + } + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.io.core.jvm479158162.get().let { "${it.group}:kotlinx-io-core:${it.version}" }) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + } + implementation(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) { + isTransitive = false + } + api(jps.com.github.ajalt.clikt.clikt.core.jvm23167398.get().let { "${it.group}:clikt-core:${it.version}" }) { + isTransitive = false + } + api(jps.com.github.ajalt.clikt.clikt.jvm1164206222.get().let { "${it.group}:clikt:${it.version}" }) { + exclude(group = "com.github.ajalt.clikt", module = "clikt-core-jvm") + exclude(group = "com.github.ajalt.clikt", module = "clikt-core") + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-jna-jvm") + exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-jna") + exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-ffm-jvm") + exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-ffm") + exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-graal-ffi-jvm") + exclude(group = "com.github.ajalt.mordant", module = "mordant-jvm-graal-ffi") + } + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt b/fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt new file mode 100644 index 000000000000..12f823e4726e --- /dev/null +++ b/fleet/build/cli/srcJvmMain/fleet/buildtool/cli/main.kt @@ -0,0 +1,31 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package fleet.buildtool.cli + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.main +import com.github.ajalt.clikt.core.subcommands +import kotlin.io.path.Path +import kotlin.io.path.readLines + +class Haven : CliktCommand(name = "haven") { + override fun run(): Unit = Unit +} + +fun main(args: Array): Unit = Haven().subcommands( + // TODO: add commands here +).main(parseArgs(args)) + +private fun parseArgs(startupArgs: Array): List { + return when (startupArgs.size) { + 1 -> Path(startupArgs[0].removePrefixStrict("--flagfile=")).readLines() + else -> startupArgs.toList() + } +} + +private fun String.removePrefixStrict(prefix: String): String { + val result = removePrefix(prefix) + check(result != this) { + "String must start with $prefix but was: $this" + } + return result +} \ No newline at end of file diff --git a/fleet/build/codecache/BUILD.bazel b/fleet/build/codecache/BUILD.bazel new file mode 100644 index 000000000000..8588550f7dfb --- /dev/null +++ b/fleet/build/codecache/BUILD.bazel @@ -0,0 +1,34 @@ +### auto-generated section `build fleet.build.codecache` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_codecache", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "codecache", + module_name = "fleet.build.codecache", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_codecache", + deps = [ + "@lib//:kotlin-stdlib", + "//fleet/bundles", + "//fleet/codecache", + "//fleet/build/platform", + "//fleet/build/fs", + "@lib//:kotlinx-coroutines-core", + "@lib//:slf4j-api", + "@lib//:fleet-jetbrains-intellij-platform-util-zip-squashed", + "@lib//:fleet-jetbrains-intellij-idea-community-build-zip", + ] +) +### auto-generated section `build fleet.build.codecache` end \ No newline at end of file diff --git a/fleet/build/codecache/fleet.build.codecache.iml b/fleet/build/codecache/fleet.build.codecache.iml new file mode 100644 index 000000000000..7fc853dadcce --- /dev/null +++ b/fleet/build/codecache/fleet.build.codecache.iml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/codecache/gradlebuild/build.gradle.kts b/fleet/build/codecache/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..b7c97956cc34 --- /dev/null +++ b/fleet/build/codecache/gradlebuild/build.gradle.kts @@ -0,0 +1,69 @@ +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.codecache" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) { + isTransitive = false + } + implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + exclude(group = "org.slf4j", module = "slf4j-jdk14") + } + implementation(jps.com.jetbrains.intellij.platform.util.zip.squashed55996306.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + } + implementation(jps.com.jetbrains.intellij.idea.idea.community.build.zip464377771.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + } + implementation(project(":fleet.bundles")) + implementation(project(":fleet.codecache")) + implementation(project(":fleet.build.platform")) + implementation(project(":fleet.build.fs")) + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt new file mode 100644 index 000000000000..2d9a4c13aaa5 --- /dev/null +++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/HashJarsUtils.kt @@ -0,0 +1,31 @@ +package fleet.buildtool.codecache + +import java.lang.module.ModuleDescriptor +import java.lang.module.ModuleFinder +import java.nio.file.Path + +data class HashedJar( + val hash: String, + val file: Path, + val moduleDescriptor: String?, +) { + companion object { + fun fromFile(hash: String, file: Path, jdkVersionFeature: Int): HashedJar = HashedJar( + hash = hash, + file = file, + moduleDescriptor = findModuleDescriptorOrNull(file)?.serialize(jdkVersionFeature), + ) + } +} + +fun findModuleDescriptorOrNull(jarFile: Path): ModuleDescriptor? { + val allModules = ModuleFinder.of(jarFile).findAll() + return when { + allModules.isEmpty() -> null + allModules.size > 1 -> error("One jar file should have only one module: $jarFile") + else -> allModules.single().descriptor() + } +} + +fun findModuleDescriptor(jarFile: Path): ModuleDescriptor = findModuleDescriptorOrNull(jarFile) + ?: error("a module descriptor must be able to be derived from '$jarFile'") diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt new file mode 100644 index 000000000000..db9dd1cfd053 --- /dev/null +++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModuleDescriptorSerializer.kt @@ -0,0 +1,370 @@ +package fleet.buildtool.codecache + +import java.lang.module.ModuleDescriptor +import java.io.ByteArrayOutputStream +import java.io.DataOutputStream +import java.util.* + +/** + * Serialises this module descriptor into the class file format. + * + * Class file version will be inferred from the [jdkVersionFeature] parameter. + * + * If the module is "automatic" it will be converted into an "open" module. + * + * See: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html + */ +fun ModuleDescriptor.serialize(jdkVersionFeature: Int): String = let { descriptor -> + // As defined by https://www.oracle.com/java/technologies/javase/10-relnote-issues.html#Remaining + val CLASS_FILE_VERSION_MAJOR = 44 + jdkVersionFeature + val CLASS_FILE_VERSION_MINOR = 0 + + assert(CLASS_FILE_VERSION_MAJOR >= 53) { + "Modules are not supported by this class file version ($CLASS_FILE_VERSION_MAJOR.$CLASS_FILE_VERSION_MINOR)" + } + + return byteArrayBuilder { dos -> + dos.writeInt(CLASS_FILE_MAGIC) + + dos.writeShort(CLASS_FILE_VERSION_MINOR) + dos.writeShort(CLASS_FILE_VERSION_MAJOR) + + // Constant pool is filled in by its users (e.g. writeModule). + val pool = ConstantPool() + val module = byteArrayBuilder { it.writeModule(descriptor.automaticToOpen(), pool) } + + // Write constant pool. + dos.writeShort(pool.count) + for ((constant, _) in pool.constants.toList().sortedBy { it.second }) { + dos.writeConstant(constant) + } + + // Write serialised module. + dos.write(module) + }.let { + Base64.getEncoder().encodeToString(it) + } +} + +/** + * Converts an automatic module to an open module. + * + * Automatic modules are not serializable as by definition they are not represented by a `module-info.class`. We need them to be serialized + * for loading optimization at runtime. + * + * This function must be aligned with its runtime equivalent [fleet.util.modules.convertOpenToAutomatic] + */ +private fun ModuleDescriptor.automaticToOpen(): ModuleDescriptor { + val moduleDescriptor = this + if (!moduleDescriptor.isAutomatic) return moduleDescriptor + return ModuleDescriptor.newOpenModule(moduleDescriptor.name()).apply { + if (moduleDescriptor.version().isPresent) { + version(moduleDescriptor.version().get()) + } + moduleDescriptor.requires().forEach { + requires(it) + } + moduleDescriptor.provides().forEach { + provides(it) + } + packages(moduleDescriptor.packages()) + }.build() +} + +/** + * Serialises the part of the class file that pertains to a particular module (its flags, attributes, main class, etc.) + */ +private fun DataOutputStream.writeModule(descriptor: ModuleDescriptor, pool: ConstantPool) { + // Access flags. ACC_MODULE means that this class file is a module. + writeShort(ACC_MODULE) + // this_class item must be "module-info" class in a module class file. + writeShort(pool.getOrCreateClass("module-info")) + + // super_class item must be 0 in module class files. + writeShort(0) + // interfaces_count item must be 0 in module class files. + writeShort(0) + // fields_count item must be 0 in module class files. + writeShort(0) + // methods_count item must be 0 in module class files. + writeShort(0) + + + // We will write Module attribute, SourceFile attribute and, optionally, ModulePackages and ModuleMainClass + // attributes if they are present. + // + // This means the attributes count will be in the range 2 to 4. + val attributesCount = when { + descriptor.mainClass().isPresent && descriptor.packages().isNotEmpty() -> 4 + descriptor.mainClass().isPresent || descriptor.packages().isNotEmpty() -> 3 + else -> 2 + } + + writeShort(attributesCount) + + // Write SourceFile attribute. + writeAttribute("SourceFile", pool) { + it.writeSourceFileAttributeInfo("module-info.java", pool) + } + + // Write Module attribute. + writeAttribute("Module", pool) { + it.writeModuleAttributeInfo(descriptor, pool) + } + + // Write ModuleMainClass attribute if present. + descriptor.mainClass().ifPresent { mainClass -> + writeAttribute("ModuleMainClass", pool) { + it.writeModuleMainClassAttributeInfo(mainClass, pool) + } + } + + // Write ModulePackages attribute if any are present. + descriptor.packages().takeIf { it.isNotEmpty() }?.let { packages -> + writeAttribute("ModulePackages", pool) { + it.writeModulePackagesAttributeInfo(packages, pool) + } + } +} + +private inline fun DataOutputStream.writeAttribute(attributeName: String, pool: ConstantPool, body: (DataOutputStream) -> Unit) { + // attribute_info { + // u2 attribute_name_index; + // u4 attribute_length; + // u1 info[attribute_length]; + // } + + val bytes = byteArrayBuilder { body(it) } + + writeShort(pool.getOrCreateUtf8(attributeName)) + writeInt(bytes.size) + write(bytes) +} + +private fun DataOutputStream.writeModuleAttributeInfo(descriptor: ModuleDescriptor, pool: ConstantPool) { + // Write module name. + writeShort(pool.getOrCreateModule(descriptor.name())) + + // Write module flags. + var flags = 0x0000 + for (modifier in descriptor.modifiers()) { + when (modifier) { + ModuleDescriptor.Modifier.OPEN -> flags = flags or MODULE_ACC_OPEN + ModuleDescriptor.Modifier.SYNTHETIC -> flags = flags or MODULE_ACC_SYNTHETIC + ModuleDescriptor.Modifier.MANDATED -> flags = flags or MODULE_ACC_MANDATED + ModuleDescriptor.Modifier.AUTOMATIC, null -> Unit + } + } + writeShort(flags) + + // Write module version. + writeShort(descriptor.rawVersion().map { pool.getOrCreateUtf8(it) }.orElse(0)) + + // Write requires. + writeShort(descriptor.requires().size) + for (require in descriptor.requires().sorted()) { + writeShort(pool.getOrCreateModule(require.name())) + + flags = 0x0000 + for (modifier in require.modifiers()) { + when (modifier) { + ModuleDescriptor.Requires.Modifier.TRANSITIVE -> flags = flags or REQ_ACC_TRANSITIVE + ModuleDescriptor.Requires.Modifier.STATIC -> flags = flags or REQ_ACC_STATIC_PHASE + ModuleDescriptor.Requires.Modifier.SYNTHETIC -> flags = flags or REQ_ACC_SYNTHETIC + ModuleDescriptor.Requires.Modifier.MANDATED -> flags = flags or REQ_ACC_MANDATED + null -> Unit + } + } + writeShort(flags) + + writeShort(require.rawCompiledVersion().map { pool.getOrCreateUtf8(it) }.orElse(0)) + } + + // Write exports. + writeShort(descriptor.exports().size) + for (export in descriptor.exports().sorted()) { + writeShort(pool.getOrCreatePackage(export.source())) + + flags = 0x0000 + for (modifier in export.modifiers()) { + when (modifier) { + ModuleDescriptor.Exports.Modifier.SYNTHETIC -> flags = flags or EXP_ACC_SYNTHETIC + ModuleDescriptor.Exports.Modifier.MANDATED -> flags = flags or EXP_ACC_MANDATED + null -> Unit + } + } + writeShort(flags) + + writeShort(export.targets().size) + for (target in export.targets().sorted()) { + writeShort(pool.getOrCreateModule(target)) + } + } + + // Write opens. + writeShort(descriptor.opens().size) + for (open in descriptor.opens().sorted()) { + writeShort(pool.getOrCreatePackage(open.source())) + + flags = 0x0000 + for (modifier in open.modifiers()) { + when (modifier) { + ModuleDescriptor.Opens.Modifier.SYNTHETIC -> flags = flags or OP_ACC_SYNTHETIC + ModuleDescriptor.Opens.Modifier.MANDATED -> flags = flags or OP_ACC_MANDATED + null -> Unit + } + } + writeShort(flags) + + writeShort(open.targets().size) + for (target in open.targets().sorted()) { + writeShort(pool.getOrCreateModule(target)) + } + } + + // Write uses. + writeShort(descriptor.uses().size) + for (use in descriptor.uses().sorted()) { + writeShort(pool.getOrCreateClass(use)) + } + + // Write provides. + writeShort(descriptor.provides().size) + for (provide in descriptor.provides().sorted()) { + writeShort(pool.getOrCreateClass(provide.service())) + writeShort(provide.providers().size) + for (provider in provide.providers().sorted()) { + writeShort(pool.getOrCreateClass(provider)) + } + } +} + +private fun DataOutputStream.writeSourceFileAttributeInfo(fileName: String, pool: ConstantPool) { + writeShort(pool.getOrCreateUtf8(fileName)) +} + +private fun DataOutputStream.writeModuleMainClassAttributeInfo(mainClass: String, pool: ConstantPool) { + writeShort(pool.getOrCreateClass(mainClass)) +} + +private fun DataOutputStream.writeModulePackagesAttributeInfo(packages: Set, pool: ConstantPool) { + writeShort(packages.size) + for (packageName in packages.sorted()) { + writeShort(pool.getOrCreatePackage(packageName)) + } +} + +private inline fun byteArrayBuilder(body: (DataOutputStream) -> Unit): ByteArray { + return ByteArrayOutputStream().use { baos -> + DataOutputStream(baos).use { dos -> body(dos) } + + baos.toByteArray() + } +} + +private fun DataOutputStream.writeConstant(constant: Constant) { + when (constant) { + is Constant.Utf8 -> { + writeByte(ConstantPoolTag.Utf8.value) + writeUTF(constant.value) + } + + is Constant.Class -> { + writeByte(ConstantPoolTag.Class.value) + writeShort(constant.classNameIndex) + } + + is Constant.Module -> { + writeByte(ConstantPoolTag.Module.value) + writeShort(constant.moduleNameIndex) + } + + is Constant.Package -> { + writeByte(ConstantPoolTag.Package.value) + writeShort(constant.packageNameIndex) + } + } +} + +private class ConstantPool { + private var index = 1 + + val constants = mutableMapOf() + + // Specification defines constant pool count to be count of entries in the constant pool table plus one. + val count get() = constants.size + 1 + + fun getOrCreateUtf8(string: String): Int { + return constants.computeIfAbsent(Constant.Utf8(string)) { index++ } + } + + fun getOrCreateClass(className: String): Int { + val classNameIndex = getOrCreateUtf8(internalFormName(className)) + return constants.computeIfAbsent(Constant.Class(classNameIndex)) { index++ } + } + + fun getOrCreateModule(moduleName: String): Int { + // Module names are NOT encoded in the "internal form". + val moduleNameIndex = getOrCreateUtf8(moduleName) + return constants.computeIfAbsent(Constant.Module(moduleNameIndex)) { index++ } + } + + fun getOrCreatePackage(packageName: String): Int { + val packageNameIndex = getOrCreateUtf8(internalFormName(packageName)) + return constants.computeIfAbsent(Constant.Package(packageNameIndex)) { index++ } + } + + private fun internalFormName(name: String): String { + return name.replace(".", "/") + } +} + +private sealed interface Constant { + data class Utf8(val value: String) : Constant + data class Class(val classNameIndex: Int) : Constant + data class Module(val moduleNameIndex: Int) : Constant + data class Package(val packageNameIndex: Int) : Constant +} + +private enum class ConstantPoolTag(val value: Int) { + Class(7), + Fieldref(9), + Methodref(10), + InterfaceMethodref(11), + String(8), + Integer(3), + Float(4), + Long(5), + Double(6), + NameAndType(12), + Utf8(1), + MethodHandle(15), + MethodType(16), + InvokeDynamic(18), + Module(19), + Package(20), +} + +private const val CLASS_FILE_MAGIC = 0xcafebabe.toInt() + +// Access flag denoting that this class file is a module. +private const val ACC_MODULE = 0x8000 + +// Module flags. +private const val MODULE_ACC_OPEN = 0x0020 +private const val MODULE_ACC_SYNTHETIC = 0x1000 +private const val MODULE_ACC_MANDATED = 0x8000 + +// Requires flags. +private const val REQ_ACC_TRANSITIVE = 0x0020 +private const val REQ_ACC_STATIC_PHASE = 0x0040 +private const val REQ_ACC_SYNTHETIC = 0x1000 +private const val REQ_ACC_MANDATED = 0x8000 + +// Exports flags. +private const val EXP_ACC_SYNTHETIC = 0x1000 +private const val EXP_ACC_MANDATED = 0x8000 + +// Opens flags. +private const val OP_ACC_SYNTHETIC = 0x1000 +private const val OP_ACC_MANDATED = 0x8000 diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt new file mode 100644 index 000000000000..098b6b683e01 --- /dev/null +++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/ModulePacker.kt @@ -0,0 +1,393 @@ +package fleet.buildtool.codecache + +import com.intellij.util.lang.ImmutableZipFile +import fleet.buildtool.fs.extractZip +import fleet.buildtool.platform.Arch +import fleet.buildtool.platform.Platform +import fleet.buildtool.platform.toS3DistributionSlug +import kotlinx.coroutines.* +import org.slf4j.Logger +import org.jetbrains.intellij.build.io.* +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.util.concurrent.ConcurrentHashMap +import kotlin.io.path.* + +const val allowedLicenseClientConsumerModule: String = "fleet.plugins.ship.common" +private fun allowedLicenseClientConsumerJar(version: String) = "fleet.common-$version.jar" // fleet.common jar +const val licenseClientApiJpmsModuleName: String = "ls.client.api" +private const val licenseClientApiJarName = "ls-client-api.jar" +private val jnaJarNamePattern = Regex("jna-\\d.*\\.jar") +private val kotlinStdlibJarNamePattern = Regex("kotlin-stdlib-\\d.*\\.jar") +const val allowedDockCoroutinesConsumerModule: String = "fleet.dock.runtime" +const val dockCoroutinesJpmsModuleName: String = "fleet.dock.coroutines" +private fun dockCoroutinesJarName(version: String) = "fleet.dock.coroutines-$version.jar" + +private typealias Jar = Path + +data class PackedJar( + val path: Path, + val needsScrambling: Boolean, + val nativeFilesByPlatform: Map>, +) + +data class PackedModule( + val moduleName: String, + val jarFiles: List, +) + +/** + * Represents a module that can be packed as one or more jars. + */ +interface ModuleToPack { + val name: String + val filesToPack: List +} + +sealed class NativeLibraryExtractor { + /** + * Jars would be left untouched. + */ + object Noop : NativeLibraryExtractor() + + /** + * Native libraries inside jars will be extracted to [directory] + */ + data class ExtractTo(val directory: Path) : NativeLibraryExtractor() +} + +class ModulePacker( + private val directory: Path, + private val nativeLibraryExtractor: NativeLibraryExtractor, + private val version: String, + private val logger: Logger, +) { + private val cache = ConcurrentHashMap() + + private data class CacheEntry( + val jar: PackedJar, + val shadowed: Path?, + ) + + fun packModule(module: ModuleToPack): PackedModule { + val licenseClientApiJar = module.filesToPack.singleOrNullOrThrow { // direct dependency on license client? + it.name.contains(licenseClientApiJarName) + } + require(licenseClientApiJar == null || module.name == allowedLicenseClientConsumerModule) { + "module '${module.name} is not allowed to pack '$licenseClientApiJarName'" + } + val allowedLicenseClientConsumerJarName = allowedLicenseClientConsumerJar(version) + require(licenseClientApiJar == null || module.filesToPack.singleOrNull { it.name == allowedLicenseClientConsumerJarName } != null) { + "module '${module.name} shadows '$licenseClientApiJar' so it must want to pack a jar '$allowedLicenseClientConsumerJarName'" + } + + val dockCoroutinesHackJar = module.filesToPack.singleOrNullOrThrow { it.name == dockCoroutinesJarName(version) } + require(dockCoroutinesHackJar == null || module.name == allowedDockCoroutinesConsumerModule) { + "module '${module.name} is not allowed to pack '$dockCoroutinesHackJar'" + } + require(dockCoroutinesHackJar == null || module.filesToPack.singleOrNull { f -> kotlinStdlibJarNamePattern.matches(f.name) } != null) { + "Module '${module.name}' tries to shadow '$dockCoroutinesHackJar' but is not containing only a single jar matching '$kotlinStdlibJarNamePattern'" + } + + require(licenseClientApiJar == null || dockCoroutinesHackJar == null) { + "Module '${module.name}' tries to shadow both '$licenseClientApiJar' and '$dockCoroutinesHackJar', this is not allowed by our build tool, contact #fleet-platform" + } + + val toPack = module.filesToPack.filter { it != licenseClientApiJar && it != dockCoroutinesHackJar } // never pack the shadowed jar + val jars = runBlocking(Dispatchers.IO) { + toPack.mapConcurrent { path -> + val (shadowed, needsScrambling) = when { + licenseClientApiJar == null && dockCoroutinesHackJar == null -> null to false + dockCoroutinesHackJar != null && kotlinStdlibJarNamePattern.matches(path.name) -> dockCoroutinesHackJar to false + licenseClientApiJar != null && path.name == allowedLicenseClientConsumerJarName -> licenseClientApiJar to true + else -> null to false + } + pack(path, needsScrambling = needsScrambling, shadowed = shadowed) + } + } + return PackedModule( + moduleName = module.name, + jarFiles = jars, + ) + } + + /** + * Packs the given [file] into an [ImmutableZipFile] jar. + * Also, shadows the given [shadowed], if any, inside the resulting packed jar. + * Also, extracts JNA native libraries files from the resulting jar and expose them through [PackedJar.nativeFiles]. + * + * @param file either a directory of classes representing an output of compilation of a module, or a `jar` to be repacked as immutable + * @param needsScrambling whether the resulting packed jar will require scrambling in further step of the build tool + * @param shadowed if specified, a jar to shadow (extract inside) the resulting packed jar + */ + @OptIn(ExperimentalPathApi::class) + private fun pack(file: Path, needsScrambling: Boolean, shadowed: Path?): PackedJar { + logger.info("Packing/repacking '$file' (needsScrambling=$needsScrambling, shadowing=$shadowed)") + require(!file.absolutePathString().contains(licenseClientApiJarName)) { + "$licenseClientApiJarName should have been shadowed, contact #fleet-support" + } + return cache.compute(file) { fileToPack, existing -> + when (existing) { + null -> { + directory.createDirectories() + logger.debug("'{}' (needsScrambling={}, shadowing={}) not found in cache, packing it...", file, needsScrambling, shadowed) + val jar = when { + Files.isDirectory(fileToPack) -> packToImmutableJar( + path = fileToPack, + needsScrambling = needsScrambling, + shadowed = shadowed, + target = directory.resolve("${fileToPack.fileName}-$version.jar"), + logger = logger, + ) + + nativeLibraryExtractor is NativeLibraryExtractor.ExtractTo && jnaJarNamePattern.matches(fileToPack.name) -> repackToImmutableJarExtractingNativeFiles( + fileToPack, + needsScrambling, + shadowed, + targetDirectory = directory, + nativeLibrariesTargetDirectory = nativeLibraryExtractor.directory, + logger = logger, + ) + + nativeLibraryExtractor is NativeLibraryExtractor.ExtractTo && fileToPack.name.contains("kotlin-desktop-toolkit") -> repackToImmutableJarExtractingNativeFiles2( + fileToPack, + needsScrambling, + shadowed, + targetDirectory = directory, + nativeLibrariesTargetDirectory = nativeLibraryExtractor.directory, + logger = logger, + ) + + fileToPack.name.endsWith(".jar") -> packToImmutableJar( + path = fileToPack, + needsScrambling = needsScrambling, + shadowed = shadowed, + target = directory.resolve(fileToPack.name), + logger = logger, + ) + + else -> error("Cannot pack file which is neither directory or jar: '$file'") + } + + CacheEntry( + jar = jar, + shadowed = shadowed, + ) + } + + else -> { + require(needsScrambling == existing.jar.needsScrambling) { + """ + |'$file' has already been packed/repacked but with different instructions about scrambling, this is very likely a bug in the build tool, contact #fleet-support + |Existing: ${existing.jar.needsScrambling} + |Current: ${needsScrambling} + """.trimMargin() + } + require(shadowed == existing.shadowed) { + """ + |'$file' has already been packed/repacked but with different instructions about shadowing, this is very likely a bug in the build tool, contact #fleet-support + |Existing: ${existing.shadowed} + |Current: ${shadowed} + """.trimMargin() + } + existing + } + } + }?.jar ?: error("must have either found the jar in cache, or pack/repack it") + } +} + +fun packToImmutableJar(path: Path, needsScrambling: Boolean, shadowed: Path?, target: Path, logger: Logger): PackedJar { + target.deleteIfExists() + target.parent.createDirectories() + val intermediateJar = when { + path.isDirectory() -> packDirectoriesToImmutableJar(target, listOf(path)) // TODO: add direct support for directories packing in `packToImmutableJar` instead + else -> path + } + val jar = packToImmutableJar(target, listOfNotNull(shadowed, intermediateJar), logger) // order of jars list matters, shadowed content overrides original content (reason: DebugProbes.kt of shadowed `:dock-coroutines` subproject must override `kotlin-stdlib` ones) + return PackedJar( + path = jar, + needsScrambling = needsScrambling, + nativeFilesByPlatform = emptyMap(), + ) +} + +// TODO: make it more generic, not only for JNA +@OptIn(ExperimentalPathApi::class) +private fun repackToImmutableJarExtractingNativeFiles(jar: Path, needsScrambling: Boolean, shadowed: Path?, targetDirectory: Path, nativeLibrariesTargetDirectory: Path, logger: Logger): PackedJar { + require(shadowed == null) { "shadowing not supported" } + val tmp = Files.createTempDirectory("repacking").resolve(jar.name) + extractZip(archive = jar, destination = tmp, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tmp, logger = logger) + val nativeFiles = tmp.resolve("com/sun/jna").listDirectoryEntries() + .filter { it.isDirectory() } + .mapNotNull { it.listDirectoryEntries().singleOrNull() } + .filter { lib -> lib.extension in setOf("jnilib", "dylib", "so", "tbd", "dll", "a") } + .mapNotNull { lib -> + val libraryPlatform = detectNativeLibraryPlatform(lib.parent.name) + if (libraryPlatform != null) { + val target = nativeLibrariesTargetDirectory.resolve(libraryPlatform.toS3DistributionSlug()).createDirectories() + val targetFile = lib.moveTo(target.resolve(lib.name), overwrite = true) + libraryPlatform to targetFile + } + else { + lib.deleteExisting() + null + } + }.groupBy({ (platform, _) -> platform }, { (_, file) -> file }) + val jar = packDirectoriesToImmutableJar(targetDirectory.resolve(jar.name), listOf(tmp)) + tmp.deleteRecursively() + return PackedJar( + path = jar, + needsScrambling = needsScrambling, + nativeFilesByPlatform = nativeFiles, + ) +} + +// TODO: delete once `repackToImmutableJarExtractingNativeFiles` is generic +@OptIn(ExperimentalPathApi::class) +private fun repackToImmutableJarExtractingNativeFiles2(jar: Path, needsScrambling: Boolean, shadowed: Path?, targetDirectory: Path, nativeLibrariesTargetDirectory: Path, logger: Logger): PackedJar { + require(shadowed == null) { "shadowing not supported" } + val tmp = Files.createTempDirectory("repacking").resolve(jar.name) + extractZip(archive = jar, destination = tmp, cleanDestination = false, stripTopLevelFolder = false, temporaryDir = tmp, logger = logger) + val nativeFiles = tmp.listDirectoryEntries() + .filter { lib -> lib.extension in setOf("dylib", "so", "dll") } + .mapNotNull { lib -> + val libName = lib.name + val libraryPlatform = detectNativeLibraryPlatformBasedOnItsName(libName) + if (libraryPlatform != null) { + val target = nativeLibrariesTargetDirectory.resolve(libraryPlatform.toS3DistributionSlug()).createDirectories() + val targetFile = lib.moveTo(target.resolve(lib.name), overwrite = true) + libraryPlatform to targetFile + } + else { + lib.deleteExisting() + null + } + }.groupBy({ (platform, _) -> platform }, { (_, file) -> file }) + val jar = packDirectoriesToImmutableJar(targetDirectory.resolve(jar.name), listOf(tmp)) + tmp.deleteRecursively() + return PackedJar( + path = jar, + needsScrambling = needsScrambling, + nativeFilesByPlatform = nativeFiles, + ) +} + +private fun packToImmutableJar(destinationJar: Path, jars: List, logger: Logger): Jar { + require(jars.map { it.extension }.all { it == "jar" }) { "only jars are supported, got '$jars'" } + val uniqueJar = jars.singleOrNull() + when { + uniqueJar != null && ImmutableZipFile.load(uniqueJar).use { it is ImmutableZipFile } -> uniqueJar.copyTo(destinationJar) // already an immutable jar, not repacking + else -> { + val packageIndexBuilder = PackageIndexBuilder(AddDirEntriesMode.NONE) + destinationJar.parent.createDirectories() + writeZipUsingTempFile(destinationJar, packageIndexBuilder = packageIndexBuilder) { zipOut -> + val uniqueNames = HashMap() + + for (jar in jars) { + readZipFile(jar) { name, dataSupplier -> + when { // TODO: instead of fixing the FIXMEs, could we instead write a `META-INF/MANIFEST.MF` merger with heuristics and hard failures in case it's not heuristically possible to merge them? + name == "META-INF/MANIFEST.MF" && jar.name.contains("fleet.dock.coroutines") && jars.any { it.name.contains("kotlin-stdlib") } -> { // FIXME: remove this hack, we control `fleet.dock.coroutines`, could we remove the `META-INF/MANIFEST.MF` instead to avoid the conflict? + logger.warn("WARNING: '$name' from '${jar.name}' is ignored, `fleet.dock.coroutines` jar has a `META-INF/MANIFEST.MF` which conflicts with the `kotlin-stdlib` one") + } + uniqueNames.contains(name) -> { + logger.warn("WARNING: '$name' from '${jar.name}' is ignored, reason: tooling already packed the one present in '${uniqueNames[name]}'") + } + else -> { + uniqueNames[name] = jar + + packageIndexBuilder.addFile(name) + val data = dataSupplier() + zipOut.uncompressedData(name, data) + } + } + ZipEntryProcessorResult.CONTINUE + } + } + } + } + } + return destinationJar +} + +private fun packDirectoriesToImmutableJar(destinationJar: Path, directories: List): Jar { + require(directories.all { it.isDirectory() }) { "'$directories' must be a directory" } + val tmpFile = Files.createTempFile(destinationJar.fileName.toString(), "") + zip(tmpFile, directories.associateWith { "" }, overwrite = true) + return tmpFile.moveTo(destinationJar) +} + +private suspend inline fun Iterable.mapConcurrent(crossinline transform: suspend (T) -> U): List = + coroutineScope { + map { + async { transform(it) } + }.awaitAll() + } + +private fun Iterable.singleOrNullOrThrow(p: (T) -> Boolean = { true }): T? { + var single: T? = null + var found = false + for (element in this) { + if (p(element)) { + if (found) { + throw IllegalArgumentException("Collection contains more than one matching element: $single, $element") + } + single = element + found = true + } + } + return single +} + +private fun detectNativeLibraryPlatform(path: String): Platform? { + return when { + path.contains("x86-64") -> Arch.X64 + path.contains("aarch64") -> Arch.AARCH64 + else -> null + }?.let { arch -> + when { + path.startsWith("win32") -> Platform.windows(arch) + path.startsWith("linux") -> Platform.linux(arch) + path.startsWith("darwin") -> Platform.macos(arch) + else -> null + } + } +} + +/** + * Skiko and KotlinDesktopToolkit follow this pattern + */ +private fun detectNativeLibraryPlatformBasedOnItsName(libName: String): Platform? { + return when { + libName.contains("x64") -> Arch.X64 + libName.contains("arm64") -> Arch.AARCH64 + else -> null + }?.let { arch -> + when { + libName.contains("windows") -> Platform.windows(arch) + libName.contains("linux") -> Platform.linux(arch) + libName.contains("macos") -> Platform.macos(arch) + else -> null + } + } +} + +fun Iterable.patchKotlinStdlibDebugProbes(dockCoroutinesJar: Path, repackedJarDir: Path, logger: Logger): List { + // This is a dirty hack to avoid bytebuddy injection at runtime + // Business wise, it's valid, implementation wise, it's hacky as it breaks encapsulation. + // All of that is supposed to change soon with the Gradle/Bazel plans. + val (stdlibJars, others) = this.partition { kotlinStdlibJarNamePattern.matches(it.name) } + val stdlibJar = stdlibJars.singleOrNull()?.toPath() + return when { + stdlibJar != null -> { + val repackedStdlibJar = repackedJarDir.resolve(stdlibJar.name) + repackedStdlibJar.deleteIfExists() + repackedStdlibJar.parent.createDirectories() + val jar = packToImmutableJar(repackedStdlibJar, listOf(dockCoroutinesJar, stdlibJar), logger) + others + jar.toFile() + } + + else -> others + } +} \ No newline at end of file diff --git a/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt new file mode 100644 index 000000000000..8cb4362261b9 --- /dev/null +++ b/fleet/build/codecache/srcJvmMain/fleet/buildtool/codecache/PluginPart.kt @@ -0,0 +1,44 @@ +package fleet.buildtool.codecache + +import fleet.buildtool.platform.Platform +import fleet.bundles.Coordinates +import fleet.bundles.PluginDescriptor +import fleet.bundles.PluginName +import fleet.bundles.PluginVersion +import fleet.bundles.metaAsCoordinates +import java.nio.file.Path +import kotlin.io.path.name + +fun urlForPluginResource(baseUrl: String, plugin: PluginId, resourceFilename: String): String = "$baseUrl/api/fleet/download?xmlId=${plugin.name.name}&version=${plugin.version.versionString}&module=$resourceFilename" + +data class PluginId( + val name: PluginName, + val version: PluginVersion, +) + +sealed class PluginPart { + data class Bundled(val jar: HashedJar) : PluginPart() { + override fun toCoordinates(baseUrl: String, pluginId: PluginId): Coordinates = Coordinates.Remote( + url = urlForPluginResource(baseUrl, pluginId, jar.file.name), + hash = jar.hash, + ) + + override val codeCacheFile: Path = jar.file + override val relativePathInCodeCache: Path = Path.of(jar.hash, codeCacheFile.fileName.toString()) + } + + abstract fun toCoordinates(baseUrl: String, pluginId: PluginId): Coordinates + abstract val codeCacheFile: Path + + abstract val relativePathInCodeCache: Path +} + +fun PluginDescriptor.fsdaemonCoordinates(platform: Platform): Coordinates? = metaAsCoordinates(fsdaemonCoordinatesMetadataAttributeOf(platform)) +fun fsdaemonCoordinatesMetadataAttributeOf(platform: Platform): String = when (platform) { + Platform.Linux.LinuxAarch64 -> "fsd-binaries-linux_aarch64" + Platform.Linux.LinuxX64 -> "fsd-binaries-linux_x64" + Platform.MacOs.MacOsAarch64 -> "fsd-binaries-macos_aarch64" + Platform.MacOs.MacOsX64 -> "fsd-binaries-macos_x64" + Platform.Windows.WindowsAarch64 -> "fsd-binaries-windows_aarch64" + Platform.Windows.WindowsX64 -> "fsd-binaries-windows_x64" +} diff --git a/fleet/build/fleet.bzl b/fleet/build/fleet.bzl new file mode 100644 index 000000000000..991ea2a6ee3c --- /dev/null +++ b/fleet/build/fleet.bzl @@ -0,0 +1,3 @@ +load("//:fleet/build/rules/fleet_module.bzl", _fleet_module = "fleet_module") + +fleet_module = _fleet_module diff --git a/fleet/build/fs/BUILD.bazel b/fleet/build/fs/BUILD.bazel new file mode 100644 index 000000000000..e60be36a8411 --- /dev/null +++ b/fleet/build/fs/BUILD.bazel @@ -0,0 +1,67 @@ +### auto-generated section `build fleet.build.fs` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup") + +create_kotlinc_options( + name = "custom_fs", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +resourcegroup( + name = "fs_test_resources", + srcs = glob(["resourcesJvmTest/**/*"]), + strip_prefix = "resourcesJvmTest" +) + +jvm_library( + name = "fs", + module_name = "fleet.build.fs", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_fs", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:slf4j-api", + "@lib//:fleet-github-luben-zstd-jni", + "@lib//:fleet-tukaani-xz", + "@lib//:commons-compress", + "@lib//:commons-io", + "@lib//:commons-lang3", + ] +) + +jvm_library( + name = "fs_test_lib", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmTest/**/*.kt", "srcJvmTest/**/*.java", "srcJvmTest/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + resources = [":fs_test_resources"], + kotlinc_opts = ":custom_fs", + associates = [":fs"], + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:slf4j-api", + "@lib//:fleet-github-luben-zstd-jni", + "@lib//:fleet-tukaani-xz", + "//fleet/junit4", + "@lib//:kotlin-test", + "@lib//:commons-compress", + "@lib//:commons-io", + "@lib//:commons-lang3", + ] +) +### auto-generated section `build fleet.build.fs` end + +### auto-generated section `test fleet.build.fs` start +load("@community//build:tests-options.bzl", "jps_test") + +jps_test( + name = "fs_test", + runtime_deps = [":fs_test_lib"] +) +### auto-generated section `test fleet.build.fs` end \ No newline at end of file diff --git a/fleet/build/fs/fleet.build.fs.iml b/fleet/build/fs/fleet.build.fs.iml new file mode 100644 index 000000000000..23e00f3641da --- /dev/null +++ b/fleet/build/fs/fleet.build.fs.iml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/fs/gradlebuild/build.gradle.kts b/fleet/build/fs/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..dbd15b386409 --- /dev/null +++ b/fleet/build/fs/gradlebuild/build.gradle.kts @@ -0,0 +1,76 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.fs" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + exclude(group = "org.slf4j", module = "slf4j-jdk14") + } + implementation(jps.com.github.luben.zstd.jni705422806.get()) + implementation(jps.org.tukaani.xz14489971.get()) + implementation(jps.org.apache.commons.commons.compress347955106.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "commons-codec", module = "commons-codec") + exclude(group = "commons-io", module = "commons-io") + exclude(group = "org.apache.commons", module = "commons-lang3") + } + implementation(jps.commons.io.commons.io645698317.get()) + implementation(jps.org.apache.commons.commons.lang3579297339.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + } + } + sourceSets.commonTest.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.test542871666.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + } + } + sourceSets.jvmTest.dependencies { + implementation(project(":fleet.junit4")) + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/fs/resourcesJvmTest/.gitattributes b/fleet/build/fs/resourcesJvmTest/.gitattributes new file mode 100644 index 000000000000..b3fe708f8862 --- /dev/null +++ b/fleet/build/fs/resourcesJvmTest/.gitattributes @@ -0,0 +1,3 @@ +example-abi-crlf.txt eol=crlf +example-abi-lf.txt eol=lf +example-abi-cr.txt eol=cr diff --git a/fleet/build/fs/resourcesJvmTest/archive/empty_folder.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/empty_folder.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..d2b58df2002e9cd0a691cc5636070bf9808bb228 GIT binary patch literal 358 zcmV-s0h#_EiwFR#b>?UQ1JjF7EK1JEEK6me9WXF3FgG<-0Mlj=+Q7&JLPOZb3WlZz z=7uKbMrJ0a3I>KI#->IL3I;TE6C9Klmn0SeoswHtm6?~Ek(!sCk5wH|Sy~z%b0A(( zfYU<-u!^xWFflOrCMGNR2P;Gy@I0rvedl9yyR2{Mka>m3LR;BCtjyh!IPr^7YInK@JLNeNiEW+ zT{%E7{~H>h<$p6n0|N$y3L3Zt1B~$eZ)~8DTauZZYHMh2YH4X~Y++`oXJKJr2rPmv z%(+a>6?{CM9D|%aJi}b|DiTXdiuAB$S6jzGPe;>AXV0iib5Ap)WG)j+glZ10y{DkAhJ!3Pvsf0MDF2$p8)j E0J3k8ivR!s literal 0 HcmV?d00001 diff --git a/fleet/build/fs/resourcesJvmTest/archive/empty_folder.zip b/fleet/build/fs/resourcesJvmTest/archive/empty_folder.zip new file mode 100644 index 0000000000000000000000000000000000000000..a445e4af29d636fbd6b2a6f3c6458243286d5ab6 GIT binary patch literal 440 zcmWIWW@Zs#0D%Y19?@V1l;B`cU`Q-V&d4lF)i15!W?*Fb%E-U~Rva3_%fP<0^jQWB z2Y^lCVBo+oMF?n0e7vutvwv`ezFs`kKz!zH0h*hfAex+>kd*Mj*C*@)e^^IA1JeWv zW_1B+#zIAb<{#aX9ZY?SijDzl9>;kUp1B1d>JSk3J$L@9w~odM&#T%Rr}VwgdY|$3 zOHD{gs#ten;|a{r^-o)L-#mchB&Yc>w^N6C9w6oY{GZv=ynp4R|8oTTzeYn_QwM0P9hj^C z8UI)6AGFWW|GD6r|MP(cg$o1;fc|gkq1IGCWuE@c=8FE!io*Wi1?AgE`8Aco|M$A- ziDmuQ%zq^ajrw;2`u|2V>;G-F1AmeK3Q*|(U2t3et8}0^`v0YeT2uX$`uVRJE-;U9 z`$Ln)>wlF0Cc_6E|IPS60j5DJ{2ws%e@;OE*J!Tfzn0p88u{;7{~#HD|93*w{|R7) z|I;7?5u*QFdZ;zkPnnf6M;g*Ba;l zx}flX2i-KvE~zQ()KMq@Ra%Ya|8n$yZb1LnXlDJNt#+VJ{#)uFK%)P1!nOQY2x{IE z6Cqca|1Lk&n(C*_&42xB|1Uw~{68mbCcWN(yc=Xaivb|=&oUYR{fU7sdix;Sp8;ApNS|SRxBtG8P(b+#ZLSHYQ zIQs&so@FE_h$g2eBqeAl?MT-acN>AgRW$vBRAWm||E^QVqJiX)(FK_wu}>#(8}*|joAu3gf20cb4Twa_rYTi{`_0aQjH zmma98Y63F@z9I=|9;iq{b}`62WEZ2Rs*gbP;4TJcHh8^>EsW8P2IXAjJk5a;7^{HU x9B1YQIuuk!AUhOf8nQ!$QA}fFMRh2y!Ut|JE2yLawuQJERsj9D7pR?q0RU^lRyhCw literal 0 HcmV?d00001 diff --git a/fleet/build/fs/resourcesJvmTest/archive/readonly.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/readonly.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..10c4416fb8aa551da1422cea36818f80fd0ded10 GIT binary patch literal 454 zcmV;%0XhC3iwFR}z~*QG1MQaGPQox0#|w!u;>J0s_2-V0!x_`LEv~{qZO=skEQyjFr$CNU>xE8x&xU< z!u78KcEgtIMNe{GlaY|0GN5fqVdbuRS1P zKa67!c^|s2uUP*9mHFqo&{h4n5mfz`Xh?Qa0mk(oSgQZ&`fsRz!5CBZ-zNB0|1nMu wIAu&`Le+o&9}=7DCoI!HP&)hmGY;zg-vl4oR{~Th6pCc=4ziXu8vq&r0JWIk1poj5 literal 0 HcmV?d00001 diff --git a/fleet/build/fs/resourcesJvmTest/archive/readonly.zip b/fleet/build/fs/resourcesJvmTest/archive/readonly.zip new file mode 100644 index 0000000000000000000000000000000000000000..5707c4bc0c58342e0621331b09fe27cccb9c8933 GIT binary patch literal 485 zcmWIWW@Zs#-~d9Obso_SNPveyfuSfhF(p4Qr&6z^qNKEfn}Lz#Dtw~-sDjuVIja9Sb`v?M;>*U{NOI6_}9 zo>==P0_{sq5KT@`NJ{wN>l5~YKdd95foXySv$}vZW1*rz^N()H4yHauMaKX&kK;TF z&)k9!bqI+2o;!clTSw!B=T&WuQ~KU#z0Y|2r6!~#Rjj)(aasAJQY*02kC;Sm4FfuR zG1%#hOd`yP5JL7A4}%0y1q7%xf@r+%XJudng;#(#s)@+9iKCd93N(?}z=0di$_8=> P6A-Qk(h9)nVqgFO^Er5= literal 0 HcmV?d00001 diff --git a/fleet/build/fs/resourcesJvmTest/archive/single_symlink.tar.gz b/fleet/build/fs/resourcesJvmTest/archive/single_symlink.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..3f32e088222ddc73d7c9c2a2241f1798c7919b68 GIT binary patch literal 1048 zcmV+z1n2u7iwFR_bmnLP1MOH_h#OTHK3Oc<@jditwP_sg`y8)rS{2_ z3(aoaWVh;UkmQ4$|K#xh=bSnJe1Fat$_*R`P18sa$cBYYOS62_wm>ohjTjy=0tZQ& zVG+v$QgD8204!LxQpQ^`Doa^Y&FE&Xu&Ez*OQm|u(|V43_J0D(O&!Arkk)O=uG-G5 zfzJc-eE0k#hHK?tpgG9n{0j`^07*NuosAgq{DWP49|R)+L==^rjFNL?3-vLu8-G{; zTpwbKzq?kqH>FROXPUiwgzH({`Lx63f!6pV0PIr>2Bj2>d7YB=svR`df|fPY&3J^H zM*(=U?UPq5d%@B*O|kWZQ_7meKy5!mw-xN8JfDjk4l7ALe~dNsOs&kZTqRXp zTr6nTQaNvFikjq`b6mj9!vH+qo}*YURCH4@RUItYspmY07>;KdBzpLYbt6*``tsxl zd+YYMeXp&4o4)ke6MKGHz53^}`pr{!ugPlc)z_(7;-wR(PJi^rD`(Y_pRb(Sef*sl z_uP2n#T=C41zd`3NV`CNGU-7~qhukx{nq^sGV zqMuy9B_ELXUs$29-TeK^`};4heSY8>`|0H$@1DQy8UbtM@f7vlt@J-iKjofMsz-E1 z)5~tB>cAHK$FvtZ_zw|_`Ct8Sx3M_}oc}B$4LfV-UIq!G$nre&Py)@eh$93TDor#uQDH3wEU( z^V?&g$YFnEB3Te8#$%y~Y?l0%sRd>;%v6aJrwK;nyB*MdJ!;>I4gq`StD{7FHFL^P ze02oMrVyWPbbW--^)>07ptIT%xJdL^fBnyhSDTwL!g3l#9X{O z*p8x4^p~oRfO4$rx3SgzkET2Ce>~#g@cy?GP?BoDw=hH>QP+R@teJC||2_3T%dg`< zzI9;!Lrxg-e@Czl{Rc{*q5uA-;<})%{~8e|Ktoq;fU7;ex!(G>x@L&&VZHuyst#<) ze}?YhKjh$0|Lq7kx>NsgSpRth5kdy;paksqISeW&$5`LS_W%FC$A7+l|7V?-CIip^ SA%+-YaPb%V0G`GG9smIHY#P%5 literal 0 HcmV?d00001 diff --git a/fleet/build/fs/resourcesJvmTest/archive/single_symlink.zip b/fleet/build/fs/resourcesJvmTest/archive/single_symlink.zip new file mode 100644 index 0000000000000000000000000000000000000000..9d6408b5dd4b4719868debc273ea09194b547efc GIT binary patch literal 895 zcmWIWW@Zs#0D;qa9?_n=mN0Mu*&xippumumnU}3sQc+S`!Og(P@|BT+0jxMQgqMN+ zeZaE}7%oXHN>42TsSE&{#{txR4rm_8To~X2nukz_&BQN26Tbp6i1zmJVq*yKX6HEM zsA9?rG=mw4aT_iQG(0}u*U{NOI6_}9o;Z8{0qsdn5KT@`NJ{wN>l5~YKdd95foXyS zv$}w^p@G3UmP-zHk2Bd8H6(j@2(++XI=F%{MaM1p5R>Th^aKI_bLX#m>u8+tyvpsl zR-^E&_c{NQXS~~An6nEp@$dw3m|AuoRb6^?rKV=5>dI4$2Lr0_G`grVH(NTz=4E** z1^6xs-L}5lKPf27Z%<%emRst?jS~waqZ}(M>%0m>1N{oUl8PpA9_e9rXqw&VD3a*r z7+4pX6_*rOq~)CHIcr_()`c58CobH%wDsijhIB~<-|dsGY;pXe{_)r4_DlWTo?QEF zYrj4}^dxrE{&lS??V;u`LftISL~O`Aw|$51Ngs1P-3M=8Y!t4$X7T?Czx!;>HSE(h z@*{t4U0waSzxney$?|>H(z_LJ{<;%3zjd;CihcZi;bRXgO+QzCSaf^6^AcNCJ{gl8 z-yWS_x>Cq}-uek)`hl?(Pp^Ml-{Zq`UexN+7r#Az?BFQ;jGY4*-_k7y (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)V public final fun component1 ()Lfleet/dock/api/DockContextElement$Key; public final fun component2 ()Ljava/lang/Object; public final fun copy (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)Lfleet/dock/api/DockContextElement; public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement;Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement; public fun equals (Ljava/lang/Object;)Z public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public fun getKey ()Lfleet/dock/api/DockContextElement$Key; public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public final fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/DockContextElement$Key : kotlin/coroutines/CoroutineContext$Key { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockContextElement$Key; public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement$Key; public fun equals (Ljava/lang/Object;)Z public final fun getKey ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/DockExit : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/DockExit$Companion; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun quit ()V } public final class fleet/dock/api/DockExit$Companion : kotlin/coroutines/CoroutineContext$Key { } public final class fleet/dock/api/DockExit$DefaultImpls { public static fun fold (Lfleet/dock/api/DockExit;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public abstract interface class fleet/dock/api/DockIdentity : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/DockIdentity$Companion; public abstract fun getHostId ()Ljava/lang/String; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun getUserId ()Ljava/lang/String; } public final class fleet/dock/api/DockIdentity$Companion : kotlin/coroutines/CoroutineContext$Key { } public final class fleet/dock/api/DockIdentity$DefaultImpls { public static fun fold (Lfleet/dock/api/DockIdentity;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public final class fleet/dock/api/DockPath { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockPath; public static synthetic fun copy$default (Lfleet/dock/api/DockPath;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockPath; public fun equals (Ljava/lang/Object;)Z public final fun getKey ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/DockPaths : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/DockPaths$Companion; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun getPath (Lfleet/dock/api/DockPath;)Ljava/lang/Object; } public final class fleet/dock/api/DockPaths$Companion : kotlin/coroutines/CoroutineContext$Key { } public final class fleet/dock/api/DockPaths$DefaultImpls { public static fun fold (Lfleet/dock/api/DockPaths;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public abstract interface class fleet/dock/api/LocalStorage : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/LocalStorage$Companion; public abstract fun get (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer; public fun getGlobal ()Lfleet/dock/api/LocalStorage; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun put (Lfleet/dock/api/LocalStorageKey;Ljava/nio/ByteBuffer;)V public abstract fun remove (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer; } public final class fleet/dock/api/LocalStorage$Companion : kotlin/coroutines/CoroutineContext$Key { public final fun withPrefix (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class fleet/dock/api/LocalStorage$DefaultImpls { public static fun fold (Lfleet/dock/api/LocalStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public final class fleet/dock/api/LocalStorageKey { public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/LocalStorageKey; public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageKey; public fun equals (Ljava/lang/Object;)Z public final fun getKey ()Ljava/lang/String; public final fun getVersion ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/LocalStorageKt { public static final fun getString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;)Ljava/lang/String; public static final fun putString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;)V } public final class fleet/dock/api/LocalStorageState : kotlin/coroutines/CoroutineContext$Element { public fun (Lfleet/dock/api/LocalStorageState$Key;Lkotlinx/coroutines/flow/MutableStateFlow;)V public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public fun getKey ()Lfleet/dock/api/LocalStorageState$Key; public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public final fun getState ()Lkotlinx/coroutines/flow/MutableStateFlow; public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public final class fleet/dock/api/LocalStorageState$Key : kotlin/coroutines/CoroutineContext$Key { public fun (Lfleet/dock/api/LocalStorageKey;)V public final fun component1 ()Lfleet/dock/api/LocalStorageKey; public final fun copy (Lfleet/dock/api/LocalStorageKey;)Lfleet/dock/api/LocalStorageState$Key; public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageState$Key;Lfleet/dock/api/LocalStorageKey;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageState$Key; public fun equals (Ljava/lang/Object;)Z public final fun getKey ()Lfleet/dock/api/LocalStorageKey; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/RecentShipInfo { public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V public final fun component1 ()Lfleet/dock/api/ShipType; public final fun component2 ()Lfleet/dock/api/ShipParams; public final fun component3 ()Lfleet/dock/api/ShipState; public final fun component4 ()Lfleet/dock/api/ShipDescriptor; public final fun component5 ()Ljava/util/Map; public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/RecentShipInfo; public static synthetic fun copy$default (Lfleet/dock/api/RecentShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RecentShipInfo; public fun equals (Ljava/lang/Object;)Z public final fun getMeta ()Ljava/util/Map; public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; public final fun getShipParams ()Lfleet/dock/api/ShipParams; public final fun getShipState ()Lfleet/dock/api/ShipState; public final fun getShipType ()Lfleet/dock/api/ShipType; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/RecentShips { public abstract fun forget (Lfleet/dock/api/ShipId;)V public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow; } public final class fleet/dock/api/RunningShipInfo { public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)V public final fun component1 ()Lfleet/dock/api/ShipType; public final fun component2 ()Lfleet/dock/api/ShipParams; public final fun component3 ()Lfleet/dock/api/ShipDescriptor; public final fun component4 ()Lfleet/dock/api/ShipHandle; public final fun component5 ()Ljava/util/Map; public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)Lfleet/dock/api/RunningShipInfo; public static synthetic fun copy$default (Lfleet/dock/api/RunningShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RunningShipInfo; public fun equals (Ljava/lang/Object;)Z public final fun getHandle ()Lfleet/dock/api/ShipHandle; public final fun getMeta ()Ljava/util/Map; public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; public final fun getShipParams ()Lfleet/dock/api/ShipParams; public final fun getShipType ()Lfleet/dock/api/ShipType; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/SelectedThemeState : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/SelectedThemeState$Companion; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun getTheme ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun setTheme (Lfleet/dock/api/ThemeId;)V } public final class fleet/dock/api/SelectedThemeState$Companion : kotlin/coroutines/CoroutineContext$Key { } public final class fleet/dock/api/SelectedThemeState$DefaultImpls { public static fun fold (Lfleet/dock/api/SelectedThemeState;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public final class fleet/dock/api/SelectedThemeStateKt { public static final fun getCurrent (Lfleet/dock/api/SelectedThemeState;)Lfleet/dock/api/ThemeId; } public abstract interface class fleet/dock/api/Ship { public abstract fun getMeta ()Ljava/util/Map; public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V public static synthetic fun perform$default (Lfleet/dock/api/Ship;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V } public final class fleet/dock/api/ShipBundle { public fun (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V public final fun component1 ()Ljava/lang/ModuleLayer; public final fun component2 ()Ljava/util/Map; public final fun component3 ()Lfleet/dock/api/ShipDescriptor; public final fun component4 ()Ljava/util/Map; public final fun copy (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/ShipBundle; public static synthetic fun copy$default (Lfleet/dock/api/ShipBundle;Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipBundle; public fun equals (Ljava/lang/Object;)Z public final fun getLayers ()Ljava/util/Map; public final fun getMeta ()Ljava/util/Map; public final fun getModuleLayer ()Ljava/lang/ModuleLayer; public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipDescriptor { public fun (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lfleet/dock/api/ShipVersion; public final fun component3 ()Ljava/util/Map; public final fun component4 ()Lfleet/dock/api/ShipSignature; public final fun component5 ()Ljava/util/Map; public final fun copy (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)Lfleet/dock/api/ShipDescriptor; public static synthetic fun copy$default (Lfleet/dock/api/ShipDescriptor;Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipDescriptor; public fun equals (Ljava/lang/Object;)Z public final fun getLayers ()Ljava/util/Map; public final fun getMeta ()Ljava/util/Map; public final fun getName ()Ljava/lang/String; public final fun getSignature ()Lfleet/dock/api/ShipSignature; public final fun getVersion ()Lfleet/dock/api/ShipVersion; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/ShipHandle { public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun shipIfCompleted ()Lfleet/dock/api/Ship; public abstract fun shutdown ()V } public final class fleet/dock/api/ShipId { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipId; public static synthetic fun copy$default (Lfleet/dock/api/ShipId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipId; public fun equals (Ljava/lang/Object;)Z public final fun getId ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract class fleet/dock/api/ShipJarCoordinates { } public final class fleet/dock/api/ShipJarCoordinates$Local : fleet/dock/api/ShipJarCoordinates { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Local; public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Local;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Local; public fun equals (Ljava/lang/Object;)Z public final fun getPath ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipJarCoordinates$Remote : fleet/dock/api/ShipJarCoordinates { public fun (Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Remote; public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Remote;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Remote; public fun equals (Ljava/lang/Object;)Z public final fun getHash ()Ljava/lang/String; public final fun getUrl ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipJarFile { public fun (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)V public final fun component1 ()Lfleet/dock/api/ShipJarCoordinates; public final fun component2 ()Ljava/lang/String; public final fun copy (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)Lfleet/dock/api/ShipJarFile; public static synthetic fun copy$default (Lfleet/dock/api/ShipJarFile;Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarFile; public fun equals (Ljava/lang/Object;)Z public final fun getCoordinates ()Lfleet/dock/api/ShipJarCoordinates; public final fun getSerializedModuleDescriptor ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/ShipLauncher { public abstract fun withShip (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public abstract interface class fleet/dock/api/ShipLauncherProvider { public abstract fun withShipLauncher (Lfleet/dock/api/ShipBundle;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class fleet/dock/api/ShipLayer { public fun (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)V public final fun component1 ()Ljava/util/List; public final fun component2 ()Ljava/lang/ModuleLayer; public final fun component3 ()Ljava/util/Map; public final fun copy (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)Lfleet/dock/api/ShipLayer; public static synthetic fun copy$default (Lfleet/dock/api/ShipLayer;Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipLayer; public fun equals (Ljava/lang/Object;)Z public final fun getMeta ()Ljava/util/Map; public final fun getModuleLayer ()Ljava/lang/ModuleLayer; public final fun getModules ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipLayerDescriptor { public fun (Ljava/util/Set;Ljava/util/Set;)V public final fun component1 ()Ljava/util/Set; public final fun component2 ()Ljava/util/Set; public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lfleet/dock/api/ShipLayerDescriptor; public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerDescriptor;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerDescriptor; public fun equals (Ljava/lang/Object;)Z public final fun getModulePath ()Ljava/util/Set; public final fun getModules ()Ljava/util/Set; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipLayerSelector { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipLayerSelector; public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerSelector;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerSelector; public fun equals (Ljava/lang/Object;)Z public final fun getSelector ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipParams { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipParams; public static synthetic fun copy$default (Lfleet/dock/api/ShipParams;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipParams; public fun equals (Ljava/lang/Object;)Z public final fun getConfig ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract class fleet/dock/api/ShipProgressEvent { } public final class fleet/dock/api/ShipProgressEvent$Finished : fleet/dock/api/ShipProgressEvent { public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Finished; } public final class fleet/dock/api/ShipProgressEvent$Progress : fleet/dock/api/ShipProgressEvent { public fun (Ljava/lang/String;Ljava/lang/Double;)V public synthetic fun (Ljava/lang/String;Ljava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/Double; public final fun copy (Ljava/lang/String;Ljava/lang/Double;)Lfleet/dock/api/ShipProgressEvent$Progress; public static synthetic fun copy$default (Lfleet/dock/api/ShipProgressEvent$Progress;Ljava/lang/String;Ljava/lang/Double;ILjava/lang/Object;)Lfleet/dock/api/ShipProgressEvent$Progress; public fun equals (Ljava/lang/Object;)Z public final fun getFraction ()Ljava/lang/Double; public final fun getMessage ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipProgressEvent$Started : fleet/dock/api/ShipProgressEvent { public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Started; } public abstract interface class fleet/dock/api/ShipProgressReporting : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/ShipProgressReporting$Companion; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun report (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipProgressEvent;)V } public final class fleet/dock/api/ShipProgressReporting$Companion : kotlin/coroutines/CoroutineContext$Key { } public final class fleet/dock/api/ShipProgressReporting$DefaultImpls { public static fun fold (Lfleet/dock/api/ShipProgressReporting;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public abstract interface class fleet/dock/api/ShipScope { public abstract fun getShips ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun launchShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;)Lfleet/dock/api/ShipHandle; public abstract fun restartShip (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipDescriptor;)Lfleet/dock/api/ShipHandle; } public final class fleet/dock/api/ShipSignature { public fun ([B)V public final fun component1 ()[B public final fun copy ([B)Lfleet/dock/api/ShipSignature; public static synthetic fun copy$default (Lfleet/dock/api/ShipSignature;[BILjava/lang/Object;)Lfleet/dock/api/ShipSignature; public fun equals (Ljava/lang/Object;)Z public final fun getBytes ()[B public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipState { public fun (Ljava/util/Map;)V public final fun component1 ()Ljava/util/Map; public final fun copy (Ljava/util/Map;)Lfleet/dock/api/ShipState; public static synthetic fun copy$default (Lfleet/dock/api/ShipState;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipState; public fun equals (Ljava/lang/Object;)Z public final fun getData ()Ljava/util/Map; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipType { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipType; public static synthetic fun copy$default (Lfleet/dock/api/ShipType;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipType; public fun equals (Ljava/lang/Object;)Z public final fun getType ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class fleet/dock/api/ShipVersion { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipVersion; public static synthetic fun copy$default (Lfleet/dock/api/ShipVersion;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipVersion; public fun equals (Ljava/lang/Object;)Z public final fun getVersion ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class fleet/dock/api/SpaceTokensStorage : kotlin/coroutines/CoroutineContext$Element { public static final field Companion Lfleet/dock/api/SpaceTokensStorage$Companion; public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; public abstract fun getSpaceToken (Ljava/lang/String;)Ljava/lang/String; public abstract fun getTokens ()Lkotlinx/coroutines/flow/StateFlow; public abstract fun putSpaceToken (Ljava/lang/String;Ljava/lang/String;)V public abstract fun removeSpaceToken (Ljava/lang/String;)V } public final class fleet/dock/api/SpaceTokensStorage$Companion : kotlin/coroutines/CoroutineContext$Key { } public final class fleet/dock/api/SpaceTokensStorage$DefaultImpls { public static fun fold (Lfleet/dock/api/SpaceTokensStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun get (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public static fun minusKey (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public static fun plus (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; } public final class fleet/dock/api/ThemeId { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ThemeId; public static synthetic fun copy$default (Lfleet/dock/api/ThemeId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ThemeId; public fun equals (Ljava/lang/Object;)Z public final fun getIdent ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } \ No newline at end of file diff --git a/fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt b/fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt new file mode 100644 index 000000000000..bb7c60148eab --- /dev/null +++ b/fleet/build/fs/resourcesJvmTest/example-abi-crlf.txt @@ -0,0 +1,514 @@ +public abstract interface class fleet/dock/api/DockApi : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockApi$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getRecentShips ()Lfleet/dock/api/RecentShips; + public abstract fun getShipScope ()Lfleet/dock/api/ShipScope; + public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun perform$default (Lfleet/dock/api/DockApi;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V + public abstract fun withShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class fleet/dock/api/DockApi$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockApi$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockApi;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/DockContextElement : kotlin/coroutines/CoroutineContext$Element { + public fun (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)V + public final fun component1 ()Lfleet/dock/api/DockContextElement$Key; + public final fun component2 ()Ljava/lang/Object; + public final fun copy (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)Lfleet/dock/api/DockContextElement; + public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement;Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement; + public fun equals (Ljava/lang/Object;)Z + public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public fun getKey ()Lfleet/dock/api/DockContextElement$Key; + public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public final fun getValue ()Ljava/lang/Object; + public fun hashCode ()I + public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/DockContextElement$Key : kotlin/coroutines/CoroutineContext$Key { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockContextElement$Key; + public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement$Key; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/DockExit : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockExit$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun quit ()V +} + +public final class fleet/dock/api/DockExit$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockExit$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockExit;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public abstract interface class fleet/dock/api/DockIdentity : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockIdentity$Companion; + public abstract fun getHostId ()Ljava/lang/String; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getUserId ()Ljava/lang/String; +} + +public final class fleet/dock/api/DockIdentity$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockIdentity$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockIdentity;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/DockPath { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockPath; + public static synthetic fun copy$default (Lfleet/dock/api/DockPath;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockPath; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/DockPaths : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockPaths$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getPath (Lfleet/dock/api/DockPath;)Ljava/lang/Object; +} + +public final class fleet/dock/api/DockPaths$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockPaths$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockPaths;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public abstract interface class fleet/dock/api/LocalStorage : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/LocalStorage$Companion; + public abstract fun get (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer; + public fun getGlobal ()Lfleet/dock/api/LocalStorage; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun put (Lfleet/dock/api/LocalStorageKey;Ljava/nio/ByteBuffer;)V + public abstract fun remove (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer; +} + +public final class fleet/dock/api/LocalStorage$Companion : kotlin/coroutines/CoroutineContext$Key { + public final fun withPrefix (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class fleet/dock/api/LocalStorage$DefaultImpls { + public static fun fold (Lfleet/dock/api/LocalStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/LocalStorageKey { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/LocalStorageKey; + public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageKey; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public final fun getVersion ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/LocalStorageKt { + public static final fun getString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;)Ljava/lang/String; + public static final fun putString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;)V +} + +public final class fleet/dock/api/LocalStorageState : kotlin/coroutines/CoroutineContext$Element { + public fun (Lfleet/dock/api/LocalStorageState$Key;Lkotlinx/coroutines/flow/MutableStateFlow;)V + public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public fun getKey ()Lfleet/dock/api/LocalStorageState$Key; + public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public final fun getState ()Lkotlinx/coroutines/flow/MutableStateFlow; + public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/LocalStorageState$Key : kotlin/coroutines/CoroutineContext$Key { + public fun (Lfleet/dock/api/LocalStorageKey;)V + public final fun component1 ()Lfleet/dock/api/LocalStorageKey; + public final fun copy (Lfleet/dock/api/LocalStorageKey;)Lfleet/dock/api/LocalStorageState$Key; + public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageState$Key;Lfleet/dock/api/LocalStorageKey;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageState$Key; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Lfleet/dock/api/LocalStorageKey; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/RecentShipInfo { + public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V + public final fun component1 ()Lfleet/dock/api/ShipType; + public final fun component2 ()Lfleet/dock/api/ShipParams; + public final fun component3 ()Lfleet/dock/api/ShipState; + public final fun component4 ()Lfleet/dock/api/ShipDescriptor; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/RecentShipInfo; + public static synthetic fun copy$default (Lfleet/dock/api/RecentShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RecentShipInfo; + public fun equals (Ljava/lang/Object;)Z + public final fun getMeta ()Ljava/util/Map; + public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; + public final fun getShipParams ()Lfleet/dock/api/ShipParams; + public final fun getShipState ()Lfleet/dock/api/ShipState; + public final fun getShipType ()Lfleet/dock/api/ShipType; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/RecentShips { + public abstract fun forget (Lfleet/dock/api/ShipId;)V + public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow; +} + +public final class fleet/dock/api/RunningShipInfo { + public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)V + public final fun component1 ()Lfleet/dock/api/ShipType; + public final fun component2 ()Lfleet/dock/api/ShipParams; + public final fun component3 ()Lfleet/dock/api/ShipDescriptor; + public final fun component4 ()Lfleet/dock/api/ShipHandle; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)Lfleet/dock/api/RunningShipInfo; + public static synthetic fun copy$default (Lfleet/dock/api/RunningShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RunningShipInfo; + public fun equals (Ljava/lang/Object;)Z + public final fun getHandle ()Lfleet/dock/api/ShipHandle; + public final fun getMeta ()Ljava/util/Map; + public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; + public final fun getShipParams ()Lfleet/dock/api/ShipParams; + public final fun getShipType ()Lfleet/dock/api/ShipType; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/SelectedThemeState : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/SelectedThemeState$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getTheme ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun setTheme (Lfleet/dock/api/ThemeId;)V +} + +public final class fleet/dock/api/SelectedThemeState$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/SelectedThemeState$DefaultImpls { + public static fun fold (Lfleet/dock/api/SelectedThemeState;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/SelectedThemeStateKt { + public static final fun getCurrent (Lfleet/dock/api/SelectedThemeState;)Lfleet/dock/api/ThemeId; +} + +public abstract interface class fleet/dock/api/Ship { + public abstract fun getMeta ()Ljava/util/Map; + public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun perform$default (Lfleet/dock/api/Ship;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V +} + +public final class fleet/dock/api/ShipBundle { + public fun (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V + public final fun component1 ()Ljava/lang/ModuleLayer; + public final fun component2 ()Ljava/util/Map; + public final fun component3 ()Lfleet/dock/api/ShipDescriptor; + public final fun component4 ()Ljava/util/Map; + public final fun copy (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/ShipBundle; + public static synthetic fun copy$default (Lfleet/dock/api/ShipBundle;Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipBundle; + public fun equals (Ljava/lang/Object;)Z + public final fun getLayers ()Ljava/util/Map; + public final fun getMeta ()Ljava/util/Map; + public final fun getModuleLayer ()Ljava/lang/ModuleLayer; + public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipDescriptor { + public fun (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lfleet/dock/api/ShipVersion; + public final fun component3 ()Ljava/util/Map; + public final fun component4 ()Lfleet/dock/api/ShipSignature; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)Lfleet/dock/api/ShipDescriptor; + public static synthetic fun copy$default (Lfleet/dock/api/ShipDescriptor;Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipDescriptor; + public fun equals (Ljava/lang/Object;)Z + public final fun getLayers ()Ljava/util/Map; + public final fun getMeta ()Ljava/util/Map; + public final fun getName ()Ljava/lang/String; + public final fun getSignature ()Lfleet/dock/api/ShipSignature; + public final fun getVersion ()Lfleet/dock/api/ShipVersion; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/ShipHandle { + public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun shipIfCompleted ()Lfleet/dock/api/Ship; + public abstract fun shutdown ()V +} + +public final class fleet/dock/api/ShipId { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipId; + public static synthetic fun copy$default (Lfleet/dock/api/ShipId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipId; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class fleet/dock/api/ShipJarCoordinates { +} + +public final class fleet/dock/api/ShipJarCoordinates$Local : fleet/dock/api/ShipJarCoordinates { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Local; + public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Local;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Local; + public fun equals (Ljava/lang/Object;)Z + public final fun getPath ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipJarCoordinates$Remote : fleet/dock/api/ShipJarCoordinates { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Remote; + public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Remote;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Remote; + public fun equals (Ljava/lang/Object;)Z + public final fun getHash ()Ljava/lang/String; + public final fun getUrl ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipJarFile { + public fun (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)V + public final fun component1 ()Lfleet/dock/api/ShipJarCoordinates; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)Lfleet/dock/api/ShipJarFile; + public static synthetic fun copy$default (Lfleet/dock/api/ShipJarFile;Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarFile; + public fun equals (Ljava/lang/Object;)Z + public final fun getCoordinates ()Lfleet/dock/api/ShipJarCoordinates; + public final fun getSerializedModuleDescriptor ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/ShipLauncher { + public abstract fun withShip (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class fleet/dock/api/ShipLauncherProvider { + public abstract fun withShipLauncher (Lfleet/dock/api/ShipBundle;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class fleet/dock/api/ShipLayer { + public fun (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()Ljava/lang/ModuleLayer; + public final fun component3 ()Ljava/util/Map; + public final fun copy (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)Lfleet/dock/api/ShipLayer; + public static synthetic fun copy$default (Lfleet/dock/api/ShipLayer;Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipLayer; + public fun equals (Ljava/lang/Object;)Z + public final fun getMeta ()Ljava/util/Map; + public final fun getModuleLayer ()Ljava/lang/ModuleLayer; + public final fun getModules ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipLayerDescriptor { + public fun (Ljava/util/Set;Ljava/util/Set;)V + public final fun component1 ()Ljava/util/Set; + public final fun component2 ()Ljava/util/Set; + public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lfleet/dock/api/ShipLayerDescriptor; + public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerDescriptor;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerDescriptor; + public fun equals (Ljava/lang/Object;)Z + public final fun getModulePath ()Ljava/util/Set; + public final fun getModules ()Ljava/util/Set; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipLayerSelector { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipLayerSelector; + public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerSelector;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerSelector; + public fun equals (Ljava/lang/Object;)Z + public final fun getSelector ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipParams { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipParams; + public static synthetic fun copy$default (Lfleet/dock/api/ShipParams;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipParams; + public fun equals (Ljava/lang/Object;)Z + public final fun getConfig ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class fleet/dock/api/ShipProgressEvent { +} + +public final class fleet/dock/api/ShipProgressEvent$Finished : fleet/dock/api/ShipProgressEvent { + public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Finished; +} + +public final class fleet/dock/api/ShipProgressEvent$Progress : fleet/dock/api/ShipProgressEvent { + public fun (Ljava/lang/String;Ljava/lang/Double;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/Double; + public final fun copy (Ljava/lang/String;Ljava/lang/Double;)Lfleet/dock/api/ShipProgressEvent$Progress; + public static synthetic fun copy$default (Lfleet/dock/api/ShipProgressEvent$Progress;Ljava/lang/String;Ljava/lang/Double;ILjava/lang/Object;)Lfleet/dock/api/ShipProgressEvent$Progress; + public fun equals (Ljava/lang/Object;)Z + public final fun getFraction ()Ljava/lang/Double; + public final fun getMessage ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipProgressEvent$Started : fleet/dock/api/ShipProgressEvent { + public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Started; +} + +public abstract interface class fleet/dock/api/ShipProgressReporting : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/ShipProgressReporting$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun report (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipProgressEvent;)V +} + +public final class fleet/dock/api/ShipProgressReporting$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/ShipProgressReporting$DefaultImpls { + public static fun fold (Lfleet/dock/api/ShipProgressReporting;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public abstract interface class fleet/dock/api/ShipScope { + public abstract fun getShips ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun launchShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;)Lfleet/dock/api/ShipHandle; + public abstract fun restartShip (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipDescriptor;)Lfleet/dock/api/ShipHandle; +} + +public final class fleet/dock/api/ShipSignature { + public fun ([B)V + public final fun component1 ()[B + public final fun copy ([B)Lfleet/dock/api/ShipSignature; + public static synthetic fun copy$default (Lfleet/dock/api/ShipSignature;[BILjava/lang/Object;)Lfleet/dock/api/ShipSignature; + public fun equals (Ljava/lang/Object;)Z + public final fun getBytes ()[B + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipState { + public fun (Ljava/util/Map;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lfleet/dock/api/ShipState; + public static synthetic fun copy$default (Lfleet/dock/api/ShipState;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipState; + public fun equals (Ljava/lang/Object;)Z + public final fun getData ()Ljava/util/Map; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipType { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipType; + public static synthetic fun copy$default (Lfleet/dock/api/ShipType;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipType; + public fun equals (Ljava/lang/Object;)Z + public final fun getType ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipVersion { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipVersion; + public static synthetic fun copy$default (Lfleet/dock/api/ShipVersion;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipVersion; + public fun equals (Ljava/lang/Object;)Z + public final fun getVersion ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/SpaceTokensStorage : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/SpaceTokensStorage$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getSpaceToken (Ljava/lang/String;)Ljava/lang/String; + public abstract fun getTokens ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun putSpaceToken (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun removeSpaceToken (Ljava/lang/String;)V +} + +public final class fleet/dock/api/SpaceTokensStorage$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/SpaceTokensStorage$DefaultImpls { + public static fun fold (Lfleet/dock/api/SpaceTokensStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/ThemeId { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ThemeId; + public static synthetic fun copy$default (Lfleet/dock/api/ThemeId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ThemeId; + public fun equals (Ljava/lang/Object;)Z + public final fun getIdent ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + diff --git a/fleet/build/fs/resourcesJvmTest/example-abi-lf.txt b/fleet/build/fs/resourcesJvmTest/example-abi-lf.txt new file mode 100644 index 000000000000..bb7c60148eab --- /dev/null +++ b/fleet/build/fs/resourcesJvmTest/example-abi-lf.txt @@ -0,0 +1,514 @@ +public abstract interface class fleet/dock/api/DockApi : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockApi$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getRecentShips ()Lfleet/dock/api/RecentShips; + public abstract fun getShipScope ()Lfleet/dock/api/ShipScope; + public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun perform$default (Lfleet/dock/api/DockApi;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V + public abstract fun withShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class fleet/dock/api/DockApi$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockApi$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockApi;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockApi;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/DockContextElement : kotlin/coroutines/CoroutineContext$Element { + public fun (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)V + public final fun component1 ()Lfleet/dock/api/DockContextElement$Key; + public final fun component2 ()Ljava/lang/Object; + public final fun copy (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;)Lfleet/dock/api/DockContextElement; + public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement;Lfleet/dock/api/DockContextElement$Key;Ljava/lang/Object;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement; + public fun equals (Ljava/lang/Object;)Z + public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public fun getKey ()Lfleet/dock/api/DockContextElement$Key; + public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public final fun getValue ()Ljava/lang/Object; + public fun hashCode ()I + public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/DockContextElement$Key : kotlin/coroutines/CoroutineContext$Key { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockContextElement$Key; + public static synthetic fun copy$default (Lfleet/dock/api/DockContextElement$Key;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockContextElement$Key; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/DockExit : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockExit$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun quit ()V +} + +public final class fleet/dock/api/DockExit$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockExit$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockExit;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockExit;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public abstract interface class fleet/dock/api/DockIdentity : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockIdentity$Companion; + public abstract fun getHostId ()Ljava/lang/String; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getUserId ()Ljava/lang/String; +} + +public final class fleet/dock/api/DockIdentity$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockIdentity$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockIdentity;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockIdentity;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/DockPath { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/DockPath; + public static synthetic fun copy$default (Lfleet/dock/api/DockPath;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/DockPath; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/DockPaths : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/DockPaths$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getPath (Lfleet/dock/api/DockPath;)Ljava/lang/Object; +} + +public final class fleet/dock/api/DockPaths$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/DockPaths$DefaultImpls { + public static fun fold (Lfleet/dock/api/DockPaths;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/DockPaths;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public abstract interface class fleet/dock/api/LocalStorage : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/LocalStorage$Companion; + public abstract fun get (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer; + public fun getGlobal ()Lfleet/dock/api/LocalStorage; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun put (Lfleet/dock/api/LocalStorageKey;Ljava/nio/ByteBuffer;)V + public abstract fun remove (Lfleet/dock/api/LocalStorageKey;)Ljava/nio/ByteBuffer; +} + +public final class fleet/dock/api/LocalStorage$Companion : kotlin/coroutines/CoroutineContext$Key { + public final fun withPrefix (Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class fleet/dock/api/LocalStorage$DefaultImpls { + public static fun fold (Lfleet/dock/api/LocalStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/LocalStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/LocalStorageKey { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/LocalStorageKey; + public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageKey; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public final fun getVersion ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/LocalStorageKt { + public static final fun getString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;)Ljava/lang/String; + public static final fun putString (Lfleet/dock/api/LocalStorage;Lfleet/dock/api/LocalStorageKey;Ljava/lang/String;)V +} + +public final class fleet/dock/api/LocalStorageState : kotlin/coroutines/CoroutineContext$Element { + public fun (Lfleet/dock/api/LocalStorageState$Key;Lkotlinx/coroutines/flow/MutableStateFlow;)V + public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public fun getKey ()Lfleet/dock/api/LocalStorageState$Key; + public synthetic fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public final fun getState ()Lkotlinx/coroutines/flow/MutableStateFlow; + public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/LocalStorageState$Key : kotlin/coroutines/CoroutineContext$Key { + public fun (Lfleet/dock/api/LocalStorageKey;)V + public final fun component1 ()Lfleet/dock/api/LocalStorageKey; + public final fun copy (Lfleet/dock/api/LocalStorageKey;)Lfleet/dock/api/LocalStorageState$Key; + public static synthetic fun copy$default (Lfleet/dock/api/LocalStorageState$Key;Lfleet/dock/api/LocalStorageKey;ILjava/lang/Object;)Lfleet/dock/api/LocalStorageState$Key; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Lfleet/dock/api/LocalStorageKey; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/RecentShipInfo { + public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V + public final fun component1 ()Lfleet/dock/api/ShipType; + public final fun component2 ()Lfleet/dock/api/ShipParams; + public final fun component3 ()Lfleet/dock/api/ShipState; + public final fun component4 ()Lfleet/dock/api/ShipDescriptor; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/RecentShipInfo; + public static synthetic fun copy$default (Lfleet/dock/api/RecentShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipState;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RecentShipInfo; + public fun equals (Ljava/lang/Object;)Z + public final fun getMeta ()Ljava/util/Map; + public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; + public final fun getShipParams ()Lfleet/dock/api/ShipParams; + public final fun getShipState ()Lfleet/dock/api/ShipState; + public final fun getShipType ()Lfleet/dock/api/ShipType; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/RecentShips { + public abstract fun forget (Lfleet/dock/api/ShipId;)V + public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow; +} + +public final class fleet/dock/api/RunningShipInfo { + public fun (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)V + public final fun component1 ()Lfleet/dock/api/ShipType; + public final fun component2 ()Lfleet/dock/api/ShipParams; + public final fun component3 ()Lfleet/dock/api/ShipDescriptor; + public final fun component4 ()Lfleet/dock/api/ShipHandle; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;)Lfleet/dock/api/RunningShipInfo; + public static synthetic fun copy$default (Lfleet/dock/api/RunningShipInfo;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipHandle;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/RunningShipInfo; + public fun equals (Ljava/lang/Object;)Z + public final fun getHandle ()Lfleet/dock/api/ShipHandle; + public final fun getMeta ()Ljava/util/Map; + public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; + public final fun getShipParams ()Lfleet/dock/api/ShipParams; + public final fun getShipType ()Lfleet/dock/api/ShipType; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/SelectedThemeState : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/SelectedThemeState$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getTheme ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun setTheme (Lfleet/dock/api/ThemeId;)V +} + +public final class fleet/dock/api/SelectedThemeState$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/SelectedThemeState$DefaultImpls { + public static fun fold (Lfleet/dock/api/SelectedThemeState;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/SelectedThemeState;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/SelectedThemeStateKt { + public static final fun getCurrent (Lfleet/dock/api/SelectedThemeState;)Lfleet/dock/api/ThemeId; +} + +public abstract interface class fleet/dock/api/Ship { + public abstract fun getMeta ()Ljava/util/Map; + public abstract fun getState ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun perform (Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun perform$default (Lfleet/dock/api/Ship;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V +} + +public final class fleet/dock/api/ShipBundle { + public fun (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)V + public final fun component1 ()Ljava/lang/ModuleLayer; + public final fun component2 ()Ljava/util/Map; + public final fun component3 ()Lfleet/dock/api/ShipDescriptor; + public final fun component4 ()Ljava/util/Map; + public final fun copy (Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;)Lfleet/dock/api/ShipBundle; + public static synthetic fun copy$default (Lfleet/dock/api/ShipBundle;Ljava/lang/ModuleLayer;Ljava/util/Map;Lfleet/dock/api/ShipDescriptor;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipBundle; + public fun equals (Ljava/lang/Object;)Z + public final fun getLayers ()Ljava/util/Map; + public final fun getMeta ()Ljava/util/Map; + public final fun getModuleLayer ()Ljava/lang/ModuleLayer; + public final fun getShipBundleDescriptor ()Lfleet/dock/api/ShipDescriptor; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipDescriptor { + public fun (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lfleet/dock/api/ShipVersion; + public final fun component3 ()Ljava/util/Map; + public final fun component4 ()Lfleet/dock/api/ShipSignature; + public final fun component5 ()Ljava/util/Map; + public final fun copy (Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;)Lfleet/dock/api/ShipDescriptor; + public static synthetic fun copy$default (Lfleet/dock/api/ShipDescriptor;Ljava/lang/String;Lfleet/dock/api/ShipVersion;Ljava/util/Map;Lfleet/dock/api/ShipSignature;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipDescriptor; + public fun equals (Ljava/lang/Object;)Z + public final fun getLayers ()Ljava/util/Map; + public final fun getMeta ()Ljava/util/Map; + public final fun getName ()Ljava/lang/String; + public final fun getSignature ()Lfleet/dock/api/ShipSignature; + public final fun getVersion ()Lfleet/dock/api/ShipVersion; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/ShipHandle { + public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun shipIfCompleted ()Lfleet/dock/api/Ship; + public abstract fun shutdown ()V +} + +public final class fleet/dock/api/ShipId { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipId; + public static synthetic fun copy$default (Lfleet/dock/api/ShipId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipId; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class fleet/dock/api/ShipJarCoordinates { +} + +public final class fleet/dock/api/ShipJarCoordinates$Local : fleet/dock/api/ShipJarCoordinates { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Local; + public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Local;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Local; + public fun equals (Ljava/lang/Object;)Z + public final fun getPath ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipJarCoordinates$Remote : fleet/dock/api/ShipJarCoordinates { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lfleet/dock/api/ShipJarCoordinates$Remote; + public static synthetic fun copy$default (Lfleet/dock/api/ShipJarCoordinates$Remote;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarCoordinates$Remote; + public fun equals (Ljava/lang/Object;)Z + public final fun getHash ()Ljava/lang/String; + public final fun getUrl ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipJarFile { + public fun (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)V + public final fun component1 ()Lfleet/dock/api/ShipJarCoordinates; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;)Lfleet/dock/api/ShipJarFile; + public static synthetic fun copy$default (Lfleet/dock/api/ShipJarFile;Lfleet/dock/api/ShipJarCoordinates;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipJarFile; + public fun equals (Ljava/lang/Object;)Z + public final fun getCoordinates ()Lfleet/dock/api/ShipJarCoordinates; + public final fun getSerializedModuleDescriptor ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/ShipLauncher { + public abstract fun withShip (Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class fleet/dock/api/ShipLauncherProvider { + public abstract fun withShipLauncher (Lfleet/dock/api/ShipBundle;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class fleet/dock/api/ShipLayer { + public fun (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()Ljava/lang/ModuleLayer; + public final fun component3 ()Ljava/util/Map; + public final fun copy (Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;)Lfleet/dock/api/ShipLayer; + public static synthetic fun copy$default (Lfleet/dock/api/ShipLayer;Ljava/util/List;Ljava/lang/ModuleLayer;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipLayer; + public fun equals (Ljava/lang/Object;)Z + public final fun getMeta ()Ljava/util/Map; + public final fun getModuleLayer ()Ljava/lang/ModuleLayer; + public final fun getModules ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipLayerDescriptor { + public fun (Ljava/util/Set;Ljava/util/Set;)V + public final fun component1 ()Ljava/util/Set; + public final fun component2 ()Ljava/util/Set; + public final fun copy (Ljava/util/Set;Ljava/util/Set;)Lfleet/dock/api/ShipLayerDescriptor; + public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerDescriptor;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerDescriptor; + public fun equals (Ljava/lang/Object;)Z + public final fun getModulePath ()Ljava/util/Set; + public final fun getModules ()Ljava/util/Set; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipLayerSelector { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipLayerSelector; + public static synthetic fun copy$default (Lfleet/dock/api/ShipLayerSelector;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipLayerSelector; + public fun equals (Ljava/lang/Object;)Z + public final fun getSelector ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipParams { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipParams; + public static synthetic fun copy$default (Lfleet/dock/api/ShipParams;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipParams; + public fun equals (Ljava/lang/Object;)Z + public final fun getConfig ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class fleet/dock/api/ShipProgressEvent { +} + +public final class fleet/dock/api/ShipProgressEvent$Finished : fleet/dock/api/ShipProgressEvent { + public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Finished; +} + +public final class fleet/dock/api/ShipProgressEvent$Progress : fleet/dock/api/ShipProgressEvent { + public fun (Ljava/lang/String;Ljava/lang/Double;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/Double; + public final fun copy (Ljava/lang/String;Ljava/lang/Double;)Lfleet/dock/api/ShipProgressEvent$Progress; + public static synthetic fun copy$default (Lfleet/dock/api/ShipProgressEvent$Progress;Ljava/lang/String;Ljava/lang/Double;ILjava/lang/Object;)Lfleet/dock/api/ShipProgressEvent$Progress; + public fun equals (Ljava/lang/Object;)Z + public final fun getFraction ()Ljava/lang/Double; + public final fun getMessage ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipProgressEvent$Started : fleet/dock/api/ShipProgressEvent { + public static final field INSTANCE Lfleet/dock/api/ShipProgressEvent$Started; +} + +public abstract interface class fleet/dock/api/ShipProgressReporting : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/ShipProgressReporting$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun report (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipProgressEvent;)V +} + +public final class fleet/dock/api/ShipProgressReporting$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/ShipProgressReporting$DefaultImpls { + public static fun fold (Lfleet/dock/api/ShipProgressReporting;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/ShipProgressReporting;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public abstract interface class fleet/dock/api/ShipScope { + public abstract fun getShips ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun launchShip (Lfleet/dock/api/ShipDescriptor;Lfleet/dock/api/ShipType;Lfleet/dock/api/ShipParams;Lfleet/dock/api/ShipId;)Lfleet/dock/api/ShipHandle; + public abstract fun restartShip (Lfleet/dock/api/ShipId;Lfleet/dock/api/ShipDescriptor;)Lfleet/dock/api/ShipHandle; +} + +public final class fleet/dock/api/ShipSignature { + public fun ([B)V + public final fun component1 ()[B + public final fun copy ([B)Lfleet/dock/api/ShipSignature; + public static synthetic fun copy$default (Lfleet/dock/api/ShipSignature;[BILjava/lang/Object;)Lfleet/dock/api/ShipSignature; + public fun equals (Ljava/lang/Object;)Z + public final fun getBytes ()[B + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipState { + public fun (Ljava/util/Map;)V + public final fun component1 ()Ljava/util/Map; + public final fun copy (Ljava/util/Map;)Lfleet/dock/api/ShipState; + public static synthetic fun copy$default (Lfleet/dock/api/ShipState;Ljava/util/Map;ILjava/lang/Object;)Lfleet/dock/api/ShipState; + public fun equals (Ljava/lang/Object;)Z + public final fun getData ()Ljava/util/Map; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipType { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipType; + public static synthetic fun copy$default (Lfleet/dock/api/ShipType;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipType; + public fun equals (Ljava/lang/Object;)Z + public final fun getType ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class fleet/dock/api/ShipVersion { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ShipVersion; + public static synthetic fun copy$default (Lfleet/dock/api/ShipVersion;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ShipVersion; + public fun equals (Ljava/lang/Object;)Z + public final fun getVersion ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class fleet/dock/api/SpaceTokensStorage : kotlin/coroutines/CoroutineContext$Element { + public static final field Companion Lfleet/dock/api/SpaceTokensStorage$Companion; + public fun getKey ()Lkotlin/coroutines/CoroutineContext$Key; + public abstract fun getSpaceToken (Ljava/lang/String;)Ljava/lang/String; + public abstract fun getTokens ()Lkotlinx/coroutines/flow/StateFlow; + public abstract fun putSpaceToken (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun removeSpaceToken (Ljava/lang/String;)V +} + +public final class fleet/dock/api/SpaceTokensStorage$Companion : kotlin/coroutines/CoroutineContext$Key { +} + +public final class fleet/dock/api/SpaceTokensStorage$DefaultImpls { + public static fun fold (Lfleet/dock/api/SpaceTokensStorage;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun get (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; + public static fun minusKey (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; + public static fun plus (Lfleet/dock/api/SpaceTokensStorage;Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; +} + +public final class fleet/dock/api/ThemeId { + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lfleet/dock/api/ThemeId; + public static synthetic fun copy$default (Lfleet/dock/api/ThemeId;Ljava/lang/String;ILjava/lang/Object;)Lfleet/dock/api/ThemeId; + public fun equals (Ljava/lang/Object;)Z + public final fun getIdent ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + diff --git a/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt new file mode 100644 index 000000000000..34f73db2d395 --- /dev/null +++ b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/ArchiveUtils.kt @@ -0,0 +1,533 @@ +package fleet.buildtool.fs + +import org.apache.commons.compress.archivers.ArchiveEntry +import org.apache.commons.compress.archivers.ArchiveOutputStream +import org.apache.commons.compress.archivers.tar.TarArchiveEntry +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream +import org.apache.commons.compress.archivers.tar.TarConstants.LF_SYMLINK +import org.apache.commons.compress.archivers.zip.UnixStat +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream +import org.apache.commons.compress.archivers.zip.ZipFile +import org.apache.commons.compress.compressors.CompressorStreamFactory +import org.apache.commons.compress.compressors.xz.XZCompressorInputStream +import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream +import org.slf4j.Logger +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.LinkOption +import java.nio.file.Path +import java.nio.file.attribute.PosixFileAttributeView +import java.nio.file.attribute.PosixFilePermission +import java.util.zip.* +import kotlin.io.path.* +import kotlin.io.path.exists +import kotlin.io.path.outputStream +import kotlin.io.path.pathString + +/** + * Like [org.gradle.kotlin.dsl.support.zipTo] but reproducible + */ +fun zip(targetZipFile: Path, directory: Path, withTopLevelFolder: Boolean) { + val filesToZip = directory.toFile() + .walkTopDown() + .filter { it.isFile } + .map { file -> + val relativePath = when (withTopLevelFolder) { + true -> file.toPath().relativeTo(directory.parent) + false -> file.toPath().relativeTo(directory) + }.invariantSeparatorsPathString // reproducible ZIP entry names regardless of the platform + relativePath to file.inputStream() + } + zip(targetZipFile, filesToZip) +} + +fun zip(targetZipFile: Path, filesToZip: Sequence>) { + ZipOutputStream(targetZipFile.outputStream()).use { zipOut -> + filesToZip + .sortedBy { (path, _) -> path } // reproducibile order of file in the archive + .forEach { (path, content) -> + zipOut.putNextEntry(ZipEntry(path).apply { + time = 0 // reproducible timestamp + }) + content.use { it.copyTo(zipOut) } + zipOut.closeEntry() + } + } +} + +fun gz(targetGzFile: Path, fileToPack: Path) { + fileToPack.inputStream().use { content -> + GZIPOutputStream(targetGzFile.outputStream()).use { gzOut -> + content.copyTo(gzOut) + } + } +} + +/** + * Extracts a ZIP archive to a specified output path. Output files will preserve symlinks and file permissions from an archive. + * + * Warning: to preserve necessary meta-data, we use [ZipFile] instead of [ZipInputStream] or [ZipArchiveInputStream], + * due to limitations of latter ([source](https://stackoverflow.com/questions/20220256/unzip-symlinks-from-a-ziparchiveinputstream-on-android)). + * This might result in performance drawbacks. Also, it's not recommended to use this method to extract large archives. + * Please use it with caution. + */ +fun extractZip( + archive: Path, + destination: Path, + stripTopLevelFolder: Boolean, + cleanDestination: Boolean, + temporaryDir: Path, + logger: Logger, +) = extract(archive, destination, stripTopLevelFolder, cleanDestination, ArchiveType.ZIP, "", temporaryDir, logger) + +/** + * Extracts a single file from a ZIP archive to a specified output path. The method assumes + * that the ZIP archive contains a single file and will log an error if directories or multiple + * files do not match the specified criteria. + * + * @param archive the path to the ZIP archive to be extracted + * @param outputFile the path where the extracted file will be written + * @param logger the logger instance used to log messages during the extraction process + * @param fileMatcher a predicate used to match the specific file to be extracted + */ +fun extractSingleFileZip(archive: Path, outputFile: Path, logger: Logger, fileMatcher: (ZipEntry) -> Boolean) { + logger.info("Extracting '$archive' to '$outputFile'") + // Ensure destination directory exists + if (Files.notExists(outputFile.parent)) { + Files.createDirectories(outputFile.parent) + } + + ZipInputStream(archive.inputStream()).use { zipInputStream -> + var entry = zipInputStream.nextEntry + if (entry == null) { + error("No entry found in '$archive', while expecting at least a single file") + } + while (entry != null) { + if (entry.isDirectory) { + error("Directory found in '$archive', while expecting a plain file archive") + } + if (fileMatcher(entry)) { + outputFile.outputStream().use { output -> + zipInputStream.copyTo(output) + } + zipInputStream.closeEntry() + break + } + + zipInputStream.closeEntry() + entry = zipInputStream.nextEntry + } + } +} + + +fun extractGz( + archive: Path, + destinationFile: Path, + logger: Logger, +) { + logger.info("Extracting '$archive' to '$destinationFile'") + + if (destinationFile.parent.notExists()) { + destinationFile.parent.createDirectories() + } + + GZIPInputStream(archive.inputStream()).use { gzipIn -> + destinationFile.outputStream().use { out -> + gzipIn.copyTo(out) + } + } +} + +fun extractTarZst( + archive: Path, + destination: Path, + stripTopLevelFolder: Boolean, + cleanDestination: Boolean, + temporaryDir: Path, + logger: Logger, +) = extractCompressedTar(archive, destination, stripTopLevelFolder, cleanDestination, CompressorStreamFactory.ZSTANDARD, temporaryDir, logger) + +fun extractTarGz( + archive: Path, + destination: Path, + stripTopLevelFolder: Boolean, + cleanDestination: Boolean, + temporaryDir: Path, + logger: Logger, + encoding: String? = null, +) = extractCompressedTar(archive, destination, stripTopLevelFolder, cleanDestination, CompressorStreamFactory.GZIP, temporaryDir, logger, encoding) + +fun extractTarXz( + archive: Path, + destination: Path, + stripTopLevelFolder: Boolean, + cleanDestination: Boolean, + temporaryDir: Path, + logger: Logger, + encoding: String? = null, +) = extractCompressedTar(archive, destination, stripTopLevelFolder, cleanDestination, CompressorStreamFactory.XZ, temporaryDir, logger, encoding) + + +private fun extractCompressedTar( + archive: Path, + destination: Path, + stripTopLevelFolder: Boolean, + cleanDestination: Boolean, + compressorName: String, + temporaryDir: Path, + logger: Logger, + encoding: String? = null, +) = extract(archive, destination, stripTopLevelFolder, cleanDestination, ArchiveType.TAR, compressorName, temporaryDir, logger, encoding) + +@OptIn(ExperimentalPathApi::class) +private fun extract( + archive: Path, + destination: Path, + stripTopLevelFolder: Boolean, + cleanDestination: Boolean, + archiveType: ArchiveType, + compressorName: String, + temporaryDir: Path, + logger: Logger, + encoding: String? = null, +) { + val tmpFolder = temporaryDir.resolve("${archive.fileName}_extracted") + tmpFolder.deleteRecursively() + tmpFolder.createDirectories() + logger.info("Extracting '$archive' to '$tmpFolder'") + + when (archiveType) { + ArchiveType.ZIP -> { + ZipFile.builder().setPath(archive).get() + .use { zipFile -> + zipFile.entries.asSequence().forEach { entry -> + extractEntry( + entry = entry, + destination = destination, + stripTopLevelFolder = stripTopLevelFolder, + isSymbolicLink = entry.isUnixSymlink, + unixMode = entry.unixMode, + symLink = zipFile.getUnixSymlink(entry), + logger = logger, + entryInputStreamProducer = { zipFile.getInputStream(entry).buffered() }, + ) + } + } + } + ArchiveType.TAR -> + archive.inputStream().buffered().use { bufferedInputStream -> + when (compressorName) { + CompressorStreamFactory.ZSTANDARD -> ZstdCompressorInputStream(bufferedInputStream) + CompressorStreamFactory.XZ -> XZCompressorInputStream(bufferedInputStream) + else -> CompressorStreamFactory().createCompressorInputStream(compressorName, bufferedInputStream) + }.use { `in` -> + TarArchiveInputStream(`in`, encoding).use { archiveInputStream -> + archiveInputStream.extractEntriesTo(stripTopLevelFolder, tmpFolder, logger) + } + } + } + } + logger.info("Extracted '$archive' to '$tmpFolder'") + logger.info("Moving '$tmpFolder' to '$destination'") + when { + cleanDestination -> destination.deleteRecursively() + else -> require(!destination.exists() || destination.isDirectory()) { "destination be a directory is it exists" } + } + destination.parent.createDirectories() + tmpFolder.copyToRecursively(destination, followLinks = false, overwrite = cleanDestination) + tmpFolder.deleteRecursively() + logger.info("Moved '$tmpFolder' to '$destination'") +} + + +private fun TarArchiveInputStream.extractEntriesTo(stripTopLevelFolder: Boolean, destination: Path, logger: Logger) { + val archiveInputStream = this + var entry = archiveInputStream.nextEntry + while (entry != null) { + extractEntry( + entry = entry, + destination = destination, + stripTopLevelFolder = stripTopLevelFolder, + isSymbolicLink = entry.isSymbolicLink, + symLink = entry.linkName, + unixMode = entry.mode, + logger = logger + ) { archiveInputStream } + entry = archiveInputStream.nextEntry + } +} + +/** + * Extracts a given archive entry to a specified destination path, applying optional transformations + * such as stripping the top-level folder, handling symbolic links, and transferring file permissions. + */ +private fun extractEntry( + entry: ArchiveEntry, destination: Path, + stripTopLevelFolder: Boolean, + isSymbolicLink: Boolean, + symLink: String?, + unixMode: Int, + logger: Logger, + entryInputStreamProducer: () -> InputStream, +) { + val relative = when { + stripTopLevelFolder -> entry.name.split("/").drop(1).joinToString("/") + else -> entry.name + } + if (relative != "") { + val destinationFile = destination.resolve(relative).normalize() + val parent = destinationFile.parent + val realParent = when { + parent.exists(LinkOption.NOFOLLOW_LINKS) && parent.isSymbolicLink() -> parent.parent.resolve(parent.readSymbolicLink()) + else -> parent + } + realParent.createDirectories() + + when { + entry.isDirectory -> if (!destinationFile.exists()) { + destinationFile.createDirectories() + } + isSymbolicLink -> { + requireNotNull(symLink) { "symLink must not be null when isSymbolicLink is true" } + destinationFile.createSymbolicLinkPointingTo(Path.of(symLink)) + } + else -> destinationFile.outputStream().use { fileOut -> + entryInputStreamProducer().copyTo(fileOut) + } + } + + restorePermissions(destinationFile, unixMode, isSymbolicLink, logger) + } +} + +private fun restorePermissions(destinationFile: Path, unixMode: Int, isSymbolicLink: Boolean, logger: Logger) { + when { + isSymbolicLink -> {} + unixMode == 0 -> {} + else -> try { + val attr = destinationFile.fileAttributesView(LinkOption.NOFOLLOW_LINKS) + attr.setPermissions(unixMode.toPosixPermissions()) + } + catch (_: UnsupportedOperationException) { + logger.debug("Could not restore permissions on {}, file system is not POSIX compliant", destinationFile) + } + } +} + +/** + * Compresses a given [source] folder to an [outputFile] in ZIP format. + * + * TODO: Make it reproducible by using the methods from `zip(Path, Path, Boolean)` method (Make it optional, not to break backwards compatibility): + * 1. Sort entries alphabetically + * 2. Use invariant separators + * 3. Reset lastModified to 0 + */ +fun zip( + source: Path, + outputFile: Path, + withTopLevelFolder: Boolean, + temporaryDir: Path, + logger: Logger, +) = compress(source, outputFile, withTopLevelFolder, ArchiveType.ZIP, compressorName = "", temporaryDir, logger) + +fun tarGz( + source: Path, + outputFile: Path, + withTopLevelFolder: Boolean, + temporaryDir: Path, + logger: Logger, +) = tarWithCompression(source, outputFile, withTopLevelFolder, CompressorStreamFactory.GZIP, temporaryDir, logger) + +fun tarZst( + source: Path, + outputFile: Path, + withTopLevelFolder: Boolean, + temporaryDir: Path, + logger: Logger, +) = tarWithCompression(source, outputFile, withTopLevelFolder, CompressorStreamFactory.ZSTANDARD, temporaryDir, logger) + +private fun tarWithCompression( + source: Path, + outputFile: Path, + withTopLevelFolder: Boolean, + compressorName: String, + temporaryDir: Path, + logger: Logger, +): Path = compress(source, outputFile, withTopLevelFolder, ArchiveType.TAR, compressorName, temporaryDir, logger) + +@OptIn(ExperimentalPathApi::class) +private fun compress( + source: Path, + outputFile: Path, + withTopLevelFolder: Boolean, + archiveType: ArchiveType, + compressorName: String, + temporaryDir: Path, + logger: Logger, +): Path { + val tmpArchive = temporaryDir.resolve("tmp_${outputFile.fileName}") + tmpArchive.deleteRecursively() + if (temporaryDir.notExists()) { + temporaryDir.createDirectories() + } + + logger.info("Compressing '$source' to '$tmpArchive'...") + tmpArchive.outputStream().buffered().use { bufferedOutputStream -> + val compressorStream = when (archiveType) { + ArchiveType.ZIP -> bufferedOutputStream // zip uses the default compression method, no need to wrap it + ArchiveType.TAR -> when (compressorName) { + CompressorStreamFactory.ZSTANDARD -> ZstdCompressorOutputStream(bufferedOutputStream) + CompressorStreamFactory.XZ -> XZCompressorOutputStream(bufferedOutputStream) + else -> CompressorStreamFactory().createCompressorOutputStream(compressorName, bufferedOutputStream) + } + } + + compressorStream.use { out -> + val archiveStream = when (archiveType) { + ArchiveType.ZIP -> ZipArchiveOutputStream(out) + ArchiveType.TAR -> TarArchiveOutputStream(out).apply { setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU) } + } as ArchiveOutputStream + + archiveStream.use { outputStream -> + when { + source.isDirectory() -> { + val base = when { + withTopLevelFolder -> { + outputStream.addEntry(source, source.name, archiveType) + "${source.name}/" + } + + else -> "./" + } + + source.walk().forEach { // must not follow symlinks + outputStream.addEntry(it, "$base${it.relativeTo(source)}", archiveType) + } + } + + else -> outputStream.addEntry(source, source.name, archiveType = archiveType) + } + } + } + } + logger.info("Compressed '$source' to '$tmpArchive'") + logger.info("Moving '$tmpArchive' to '$outputFile'") + outputFile.deleteRecursively() + outputFile.parent.createDirectories() + tmpArchive.moveTo(outputFile) + logger.info("Moved '$tmpArchive' to '$outputFile'") + return outputFile +} + +private fun ArchiveOutputStream.addEntry(path: Path, relativePathInArchive: String, archiveType: ArchiveType) { + when { + path.isSymbolicLink() -> { + when (archiveType) { + ArchiveType.TAR -> { + val entry = TarArchiveEntry(relativePathInArchive, LF_SYMLINK).also { + it.linkName = path.readSymbolicLink().pathString + when (val permissions = path.getFilePermissions(LinkOption.NOFOLLOW_LINKS)) { + FilePermissions.Other -> {} + is FilePermissions.Posix -> it.mode = permissions.toInt() + } + } + putArchiveEntry(entry) + closeArchiveEntry() + } + ArchiveType.ZIP -> { + val link = path.readSymbolicLink().pathString + val entry = ZipArchiveEntry(relativePathInArchive).also { + when (val permissions = path.getFilePermissions(LinkOption.NOFOLLOW_LINKS)) { + FilePermissions.Other -> {} + is FilePermissions.Posix -> it.unixMode = UnixStat.LINK_FLAG or permissions.toInt() + } + it.size = link.toByteArray().size.toLong() + } + putArchiveEntry(entry) + write(link.toByteArray()) + closeArchiveEntry() + } + } + } + + path.isDirectory() -> {} // this must be checked AFTER `path.isSymbolicLink()`, otherwise symlink pointing to directory will be ignored + + else -> path.inputStream().use { fileIn -> + val entry: ArchiveEntry = when (archiveType) { + ArchiveType.TAR -> TarArchiveEntry(path, relativePathInArchive).also { + when (val permissions = path.getFilePermissions()) { + FilePermissions.Other -> {} + is FilePermissions.Posix -> it.mode = permissions.toInt() + } + } + ArchiveType.ZIP -> ZipArchiveEntry(path, relativePathInArchive).also { + when (val permissions = path.getFilePermissions()) { + FilePermissions.Other -> {} + is FilePermissions.Posix -> it.unixMode = permissions.toInt() + } + } + } + putArchiveEntry(entry) + fileIn.copyTo(this) + closeArchiveEntry() + } + } +} + + +private fun Path.getFilePermissions(vararg options: LinkOption): FilePermissions { + return try { + FilePermissions.Posix(getPosixFilePermissions(*options)) + } + catch (_: UnsupportedOperationException) { + FilePermissions.Other + } +} + +internal fun Int.toPosixPermissions(): Set { + val perms = mutableSetOf() + if (this and 0b100000000 != 0) perms.add(PosixFilePermission.OWNER_READ) + if (this and 0b010000000 != 0) perms.add(PosixFilePermission.OWNER_WRITE) + if (this and 0b001000000 != 0) perms.add(PosixFilePermission.OWNER_EXECUTE) + if (this and 0b000100000 != 0) perms.add(PosixFilePermission.GROUP_READ) + if (this and 0b000010000 != 0) perms.add(PosixFilePermission.GROUP_WRITE) + if (this and 0b000001000 != 0) perms.add(PosixFilePermission.GROUP_EXECUTE) + if (this and 0b000000100 != 0) perms.add(PosixFilePermission.OTHERS_READ) + if (this and 0b000000010 != 0) perms.add(PosixFilePermission.OTHERS_WRITE) + if (this and 0b000000001 != 0) perms.add(PosixFilePermission.OTHERS_EXECUTE) + return perms +} + +internal fun Set.toInt(): Int = + map { permission -> + when (permission) { + PosixFilePermission.OWNER_READ -> 0b100000000 + PosixFilePermission.OWNER_WRITE -> 0b010000000 + PosixFilePermission.OWNER_EXECUTE -> 0b001000000 + PosixFilePermission.GROUP_READ -> 0b000100000 + PosixFilePermission.GROUP_WRITE -> 0b000010000 + PosixFilePermission.GROUP_EXECUTE -> 0b000001000 + PosixFilePermission.OTHERS_READ -> 0b000000100 + PosixFilePermission.OTHERS_WRITE -> 0b000000010 + PosixFilePermission.OTHERS_EXECUTE -> 0b000000001 + } + }.sum() + +private sealed class FilePermissions { + object Other : FilePermissions() + data class Posix(val permissions: Set) : FilePermissions() { + + fun toInt() = permissions.toInt() + } +} + +private enum class ArchiveType { + ZIP, + TAR, +} \ No newline at end of file diff --git a/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt new file mode 100644 index 000000000000..3cccd5cec4c6 --- /dev/null +++ b/fleet/build/fs/srcJvmMain/fleet/buildtool/fs/Utils.kt @@ -0,0 +1,65 @@ +package fleet.buildtool.fs + +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +import java.nio.charset.Charset +import java.nio.file.Path +import java.nio.file.attribute.PosixFilePermission +import java.security.MessageDigest +import kotlin.io.path.getPosixFilePermissions +import kotlin.io.path.readText +import kotlin.io.path.setPosixFilePermissions + +val Path.pathString: String get() = toString() + +fun sha256(bytes: ByteArray): String { + val md = MessageDigest.getInstance("SHA-256") + val digest = md.digest(bytes) + return digest.fold(StringBuilder()) { sb, it -> sb.append("%02x".format(it)) }.toString() +} + +/** + * Read content of file at [this] path and normalize it for reliable cross-platform SHA256 hashing + * + * Line endings will be normalized to '\n' to ensure the hash result produces identical result on Windows and UNIX. + */ +fun Path.readBytesForSha256(charset: Charset = Charsets.UTF_8): ByteArray = readText(charset).normaliseLineSeparators().toByteArray(charset) + +private fun String.normaliseLineSeparators() = lineSequence().joinToString("\n") + +/** + * Set POSIX file permissions of this [Path] to ensure the output is the same on all platforms. + * + * After zip extraction, execute permissions are lost on Windows which creates a different directory output. + * + * @this [Path] an extracted path from a ZIP archive, which extraction is intended to be done on all platforms reproducibly. + */ +fun Path.makeReproducibleAfterCrossOSExtraction() { + try { + val originalPermissions = getPosixFilePermissions() + val newPermissions = originalPermissions.minus(setOf(PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_EXECUTE)) + setPosixFilePermissions(newPermissions) + } catch (_: UnsupportedOperationException) { + // do nothing + } +} + +/** + * Inefficiently normalize '\r\n' and '\r' to '\n'. + */ +fun ByteBuffer.normaliseLineSeparators(): ByteBuffer = + ByteBuffer.wrap(ByteArray(remaining()).also { get(it) }.normaliseLineSeparators()) + +private fun ByteArray.normaliseLineSeparators(): ByteArray { + val result = ByteArrayOutputStream(size) + var i = 0 + while (i < size) { + when { + i + 1 < size && this[i] == '\r'.code.toByte() && this[i + 1] == '\n'.code.toByte() -> {} + this[i] == '\r'.code.toByte() -> result.write('\n'.code) + else -> result.write(this[i].toInt()) + } + i++ + } + return result.toByteArray() +} diff --git a/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt new file mode 100644 index 000000000000..dc63a541518d --- /dev/null +++ b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/ArchiveUtilsTest.kt @@ -0,0 +1,1162 @@ +package fleet.buildtool.fs + +import org.junit.Assume.assumeTrue +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.ByteArrayInputStream +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.attribute.PosixFilePermission +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream +import kotlin.io.path.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ArchiveUtilsTest { + + private val logger: Logger = LoggerFactory.getLogger(ArchiveUtilsTest::class.java) + + private lateinit var tempDir: Path + + @BeforeTest + fun setUp() { + tempDir = Files.createTempDirectory("archive-utils-test") + } + + @OptIn(ExperimentalPathApi::class) + @AfterTest + fun tearDown() { + tempDir.deleteRecursively() + } + + // ========== Zip Tests ========== + + @Test + fun `should create zip archive from directory without top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + sourceDir.resolve("file2.txt").writeText("content2") + val subDir = sourceDir.resolve("subdir").createDirectories() + subDir.resolve("file3.txt").writeText("content3") + + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + assertTrue(targetZip.exists()) + val entriesMap = readZipEntries(targetZip) + assertEquals(3, entriesMap.size) + assertEquals("content1", entriesMap["file1.txt"]) + assertEquals("content2", entriesMap["file2.txt"]) + assertEquals("content3", entriesMap["subdir/file3.txt"]) + } + + @Test + fun `should create zip archive from directory with top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + val subDir = sourceDir.resolve("subdir").createDirectories() + subDir.resolve("file2.txt").writeText("content2") + + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = true) + + // Then + assertTrue(targetZip.exists()) + val entriesMap = readZipEntries(targetZip) + assertEquals(2, entriesMap.size) + assertEquals("content1", entriesMap["source/file1.txt"]) + assertEquals("content2", entriesMap["source/subdir/file2.txt"]) + } + + @Test + fun `should create zip with sorted entries for reproducibility`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("zebra.txt").writeText("z") + sourceDir.resolve("alpha.txt").writeText("a") + sourceDir.resolve("beta.txt").writeText("b") + + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + val entryNames = readZipEntryNames(targetZip) + assertEquals(listOf("alpha.txt", "beta.txt", "zebra.txt"), entryNames) + } + + @Test + fun `should create zip with timestamp set to zero for reproducibility`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file.txt").writeText("content") + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + ZipInputStream(targetZip.inputStream()).use { zipIn -> + val entry = zipIn.nextEntry + assertEquals(0L, entry.time) + } + } + + @Test + fun `should create zip from sequence of input streams`() { + // Given + val filesToZip = sequenceOf( + "path/to/file1.txt" to ByteArrayInputStream("content1".toByteArray()), + "path/to/file2.txt" to ByteArrayInputStream("content2".toByteArray()), + "another/file3.txt" to ByteArrayInputStream("content3".toByteArray()) + ) + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, filesToZip) + + // Then + val entriesMap = readZipEntries(targetZip) + assertEquals(3, entriesMap.size) + assertEquals("content1", entriesMap["path/to/file1.txt"]) + assertEquals("content2", entriesMap["path/to/file2.txt"]) + assertEquals("content3", entriesMap["another/file3.txt"]) + } + + @Test + fun `should create empty zip when source directory is empty`() { + // Given + val sourceDir = tempDir.resolve("empty").createDirectories() + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + assertTrue(targetZip.exists()) + val entriesMap = readZipEntries(targetZip) + assertEquals(0, entriesMap.size) + } + + @Test + fun `should create zip from a single file without top level folder`() { + // Given + val sourceDir = tempDir.resolve("source.txt").apply { writeText("content") } + val tmpDir = tempDir.resolve("tmp") + val outputFile = tempDir.resolve("output.zip") + + val extractDir = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + zip(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + extractZip(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(extractDir.resolve("source.txt").exists()) + assertEquals(extractDir.resolve("source.txt").readText(), "content") + } + + @Test + fun `should use invariant path separators for reproducibility`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + val subDir = sourceDir.resolve("sub1").resolve("sub2").createDirectories() + subDir.resolve("file.txt").writeText("content") + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + val entryNames = readZipEntryNames(targetZip) + // Should use forward slashes regardless of platform + assertTrue(entryNames.any { it.contains("sub1/sub2/file.txt") }) + } + + @Test + fun `should handle mixed content in zip`() { + assumePosixFileSystem() + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + + // Regular files + sourceDir.resolve("text.txt").writeText("text") + sourceDir.resolve("binary.bin").writeBytes(ByteArray(100) { it.toByte() }) + + // Directories + val nestedDir = sourceDir.resolve("nested/dir").createDirectories() + nestedDir.resolve("nested.txt").writeText("nested") + + // Symlinks (if supported) + val target = sourceDir.resolve("target.txt").apply { writeText("target") } + sourceDir.resolve("link.txt").createSymbolicLinkPointingTo(target) + + val outputFile = tempDir.resolve("output.zip") + val tmpDir = tempDir.resolve("tmp") + + // When + zip(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + val extractDir = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + extractZip(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + assertTrue(extractDir.resolve("text.txt").exists()) + assertTrue(extractDir.resolve("binary.bin").exists()) + assertTrue(extractDir.resolve("nested/dir/nested.txt").exists()) + assertEquals("text", extractDir.resolve("text.txt").readText()) + assertEquals("nested", extractDir.resolve("nested/dir/nested.txt").readText()) + val symlink = extractDir.resolve("link.txt") + assertTrue(symlink.exists()) + assertTrue(symlink.isSymbolicLink()) + assertEquals(symlink.readSymbolicLink(), target) + } + + @Test + fun `should extract manually created symlink from zip`() { + val extractDir = tempDir.resolve("extracted") + val zipFile = getFileFromResources("archive/single_symlink.zip") + val tmpDir = tempDir.resolve("tmp") + + extractZip(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + val symlink = extractDir.resolve("link.txt") + assertTrue(symlink.exists()) + assertTrue(symlink.isSymbolicLink()) + } + + @Test + fun `should extract empty folder from assets zip`() { + val extractDir = tempDir.resolve("extracted") + val zipFile = getFileFromResources("archive/empty_folder.zip") + val tmpDir = tempDir.resolve("tmp") + + extractZip(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + assertTrue(extractDir.resolve("archive").exists()) + } + + @Test + fun `should handle mixed content in assets zip`() { + // Given + val archiveFile = getFileFromResources("archive/mixed.zip") + val tmpDir = tempDir.resolve("tmp") + + // Then + val extractDir = tempDir.resolve("extracted") + extractZip(archiveFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + assertTrue(extractDir.resolve("text.txt").exists()) + assertTrue(extractDir.resolve("binary.bin").exists()) + assertTrue(extractDir.resolve("nested/nested.txt").exists()) + assertEquals("abc", extractDir.resolve("text.txt").readText().trim()) + assertEquals("nested", extractDir.resolve("nested/nested.txt").readText().trim()) + val symlink = extractDir.resolve("link.txt") + assertTrue(symlink.exists()) + assertTrue(symlink.isSymbolicLink()) + } + + // ========== Gzip Tests ========== + + @Test + fun `should create gzip archive from single file`() { + // Given + val sourceFile = tempDir.resolve("source.txt").apply { + writeText("This is test content for gzip compression") + } + val targetGz = tempDir.resolve("output.txt.gz") + + // When + gz(targetGz, sourceFile) + + // Then + assertTrue(targetGz.exists()) + assertTrue(targetGz.fileSize() > 0) + } + + @Test + fun `should compress and decompress file correctly`() { + // Given + val content = "Test content for round-trip compression" + val sourceFile = tempDir.resolve("source.txt").apply { writeText(content) } + val gzFile = tempDir.resolve("compressed.gz") + val decompressedFile = tempDir.resolve("decompressed.txt") + + // When + gz(gzFile, sourceFile) + extractGz(gzFile, decompressedFile, logger) + + // Then + assertEquals(content, decompressedFile.readText()) + } + + @Test + fun `should handle empty file compression`() { + // Given + val sourceFile = tempDir.resolve("empty.txt").apply { writeText("") } + val targetGz = tempDir.resolve("empty.gz") + + // When + gz(targetGz, sourceFile) + + // Then + assertTrue(targetGz.exists()) + assertTrue(targetGz.fileSize() > 0) // GZ header is still present + } + + // ========== Extract Zip Tests ========== + + @Test + fun `should extract zip archive to destination`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + createTestZip(zipFile, mapOf( + "file1.txt" to "content1", + "dir/file2.txt" to "content2" + )) + val destination = tempDir.resolve("extracted") + + // When + extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger) + + // Then + assertTrue(destination.resolve("file1.txt").exists()) + assertEquals("content1", destination.resolve("file1.txt").readText()) + assertTrue(destination.resolve("dir/file2.txt").exists()) + assertEquals("content2", destination.resolve("dir/file2.txt").readText()) + } + + @Test + fun `should create destination directory if not exists`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + createTestZip(zipFile, mapOf("file.txt" to "content")) + val destination = tempDir.resolve("non-existent/extracted") + + // When + extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger) + + // Then + assertTrue(destination.exists()) + assertTrue(destination.resolve("file.txt").exists()) + } + + @Test + fun `should extract directories from zip`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + ZipOutputStream(zipFile.outputStream()).use { zipOut -> + zipOut.putNextEntry(ZipEntry("emptydir/")) + zipOut.closeEntry() + zipOut.putNextEntry(ZipEntry("emptydir/file.txt")) + zipOut.write("content".toByteArray()) + zipOut.closeEntry() + } + val destination = tempDir.resolve("extracted") + + // When + extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger) + + // Then + assertTrue(destination.resolve("emptydir").isDirectory()) + assertTrue(destination.resolve("emptydir/file.txt").exists()) + } + + @Test + fun `should handle zip with nested directories`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + createTestZip(zipFile, mapOf( + "level1/level2/level3/file.txt" to "deep content" + )) + val destination = tempDir.resolve("extracted") + + // When + extractZip(zipFile, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger) + + // Then + assertTrue(destination.resolve("level1/level2/level3/file.txt").exists()) + assertEquals("deep content", destination.resolve("level1/level2/level3/file.txt").readText()) + } + + // ========== Extract Single File Zip Tests ========== + + @Test + fun `should extract single file matching the predicate`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + createTestZip(zipFile, mapOf( + "file1.txt" to "content1", + "file2.txt" to "content2", + "target.txt" to "target content" + )) + val outputFile = tempDir.resolve("extracted.txt") + + // When + extractSingleFileZip(zipFile, outputFile, logger) { entry -> + entry.name == "target.txt" + } + + // Then + assertTrue(outputFile.exists()) + assertEquals("target content", outputFile.readText()) + } + + @Test + fun `should create parent directories if not exist`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + createTestZip(zipFile, mapOf("file.txt" to "content")) + val outputFile = tempDir.resolve("non-existent/dir/output.txt") + + // When + extractSingleFileZip(zipFile, outputFile, logger) { true } + + // Then + assertTrue(outputFile.exists()) + assertEquals("content", outputFile.readText()) + } + + @Test + fun `should throw error when singleZip is empty`() { + // Given + val zipFile = tempDir.resolve("empty.zip") + ZipOutputStream(zipFile.outputStream()).use { } + val outputFile = tempDir.resolve("output.txt") + + // When/Then + val exception = assertFails { + extractSingleFileZip(zipFile, outputFile, logger) { true } + } + assertContains(exception.message ?: "", "No entry found") + } + + @Test + fun `should throw error when singleZip contains directory`() { + // Given + val zipFile = tempDir.resolve("archive.zip") + ZipOutputStream(zipFile.outputStream()).use { zipOut -> + zipOut.putNextEntry(ZipEntry("dir/")) + zipOut.closeEntry() + } + val outputFile = tempDir.resolve("output.txt") + + // When/Then + val exception = assertFails { + extractSingleFileZip(zipFile, outputFile, logger) { true } + } + assertContains(exception.message ?: "", "Directory found") + } + + // ========== Extract Gzip Tests ========== + + @Test + fun `should extract gzip archive`() { + // Given + val content = "Test content for extraction" + val sourceFile = tempDir.resolve("source.txt").apply { writeText(content) } + val gzFile = tempDir.resolve("archive.gz") + gz(gzFile, sourceFile) + val destination = tempDir.resolve("extracted.txt") + + // When + extractGz(gzFile, destination, logger) + + // Then + assertTrue(destination.exists()) + assertEquals(content, destination.readText()) + } + + @Test + fun `should create parent directory if not exists for gzip extraction`() { + // Given + val sourceFile = tempDir.resolve("source.txt").apply { writeText("content") } + val gzFile = tempDir.resolve("archive.gz") + gz(gzFile, sourceFile) + val destination = tempDir.resolve("non-existent/dir/extracted.txt") + + // When + extractGz(gzFile, destination, logger) + + // Then + assertTrue(destination.exists()) + assertEquals("content", destination.readText()) + } + + // ========== POSIX Permissions Tests ========== + + @Test + fun `should convert all possible POSIX permissions`() { + assumePosixFileSystem() + + // Given + val mode = 0b111111111 // rwxrwxrwx (777) + + // When + val permissions = mode.toPosixPermissions() + + // Then + assertEquals(9, permissions.size) + assertTrue(permissions.containsAll(PosixFilePermission.entries)) + } + + @Test + fun `should convert no permissions`() { + assumePosixFileSystem() + + // Given + val mode = 0b000000000 // no permissions (000) + + // When + val permissions = mode.toPosixPermissions() + + // Then + assertEquals(0, permissions.size) + } + + @Test + fun `should convert POSIX permissions to int correctly`() { + assumePosixFileSystem() + + // Given + val permissions = setOf( + PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, + PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, + PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, + PosixFilePermission.OTHERS_EXECUTE + ) + + // When + val mode = permissions.toInt() + + // Then + assertEquals(0b111101101, mode) // 755 + } + + // ========== TarGz Tests ========== + + @Test + fun `should create tar gz from directory without top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + val subDir = sourceDir.resolve("subdir").createDirectories() + subDir.resolve("file2.txt").writeText("content2") + + val outputFile = tempDir.resolve("output.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // When + tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + assertTrue(outputFile.exists()) + assertTrue(outputFile.fileSize() > 0) + } + + @Test + fun `should create tar gz from directory with top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + + val outputFile = tempDir.resolve("output.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // When + tarGz(sourceDir, outputFile, withTopLevelFolder = true, tmpDir, logger) + + // Then + assertTrue(outputFile.exists()) + } + + @Test + fun `should create tar gz from single file`() { + // Given + val sourceFile = tempDir.resolve("file.txt").apply { writeText("content") } + val outputFile = tempDir.resolve("output.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // When + tarGz(sourceFile, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + assertTrue(outputFile.exists()) + } + + @Test + fun `should return output file path from tarGz`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file.txt").writeText("content") + val outputFile = tempDir.resolve("output.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // When + val result = tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + assertEquals(outputFile, result) + } + + + @Test + fun `should handle symlink with spaces in name`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + val targetFile = sourceDir.resolve("target file.txt").apply { writeText("content") } + val symlink = sourceDir.resolve("link file.txt") + + symlink.createSymbolicLinkPointingTo(targetFile) + + val outputFile = tempDir.resolve("output.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // When + tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + val extractDir = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + extractTarGz(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + val extractedLink = extractDir.resolve("link file.txt") + assertTrue(extractedLink.exists()) + assertTrue(extractedLink.isSymbolicLink()) + } + + // ========== TarZst Tests ========== + + @Test + fun `should create tar zst from directory without top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + + val outputFile = tempDir.resolve("output.tar.zst") + val tmpDir = tempDir.resolve("tmp") + + // When + tarZst(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + assertTrue(outputFile.exists()) + assertTrue(outputFile.fileSize() > 0) + } + + @Test + fun `should create tar zst from directory with top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + + val outputFile = tempDir.resolve("output.tar.zst") + val tmpDir = tempDir.resolve("tmp") + + // When + tarZst(sourceDir, outputFile, withTopLevelFolder = true, tmpDir, logger) + + // Then + assertTrue(outputFile.exists()) + } + + @Test + fun `should create tar zst from a single file without top level folder`() { + // Given + val sourceDir = tempDir.resolve("source.txt").apply { writeText("content") } + val tmpDir = tempDir.resolve("tmp") + val outputFile = tempDir.resolve("output.tar.zst") + + val extractDir = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + tarZst(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + extractTarZst(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(extractDir.resolve("source.txt").exists()) + assertEquals(extractDir.resolve("source.txt").readText(), "content") + } + + // ========== ExtractTarGz Tests ========== + + @Test + fun `should extract tar gz without stripping top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file.txt").writeText("content") + val tarGz = tempDir.resolve("archive.tar.gz") + val tmpDir = tempDir.resolve("tmp1") + tarGz(sourceDir, tarGz, withTopLevelFolder = true, tmpDir, logger) + + val destination = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + extractTarGz(tarGz, destination, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(destination.resolve("source/file.txt").exists()) + assertEquals("content", destination.resolve("source/file.txt").readText()) + } + + @Test + fun `should extract tar gz with stripping top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file.txt").writeText("content") + val tarGz = tempDir.resolve("archive.tar.gz") + val tmpDir = tempDir.resolve("tmp1") + tarGz(sourceDir, tarGz, withTopLevelFolder = true, tmpDir, logger) + + val destination = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + extractTarGz(tarGz, destination, stripTopLevelFolder = true, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(destination.resolve("file.txt").exists()) + assertEquals("content", destination.resolve("file.txt").readText()) + } + + @Test + fun `should clean destination when cleanDestination is true`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("new.txt").writeText("new") + val tarGz = tempDir.resolve("archive.tar.gz") + val tmpDir = tempDir.resolve("tmp1") + tarGz(sourceDir, tarGz, withTopLevelFolder = false, tmpDir, logger) + + val destination = tempDir.resolve("extracted").createDirectories() + destination.resolve("old.txt").writeText("old") + + val extractTmpDir = tempDir.resolve("tmp2") + + // When + extractTarGz(tarGz, destination, stripTopLevelFolder = false, cleanDestination = true, extractTmpDir, logger) + + // Then + assertTrue(destination.resolve("new.txt").exists()) + assertFalse(destination.resolve("old.txt").exists()) + } + + @Test + fun `should preserve file structure with nested directories in tar gz`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + val nested = sourceDir.resolve("level1/level2").createDirectories() + nested.resolve("deep.txt").writeText("deep content") + val tarGz = tempDir.resolve("single_symlink.tar.gz") + val tmpDir = tempDir.resolve("tmp1") + tarGz(sourceDir, tarGz, withTopLevelFolder = false, tmpDir, logger) + + val destination = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + extractTarGz(tarGz, destination, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(destination.resolve("level1/level2/deep.txt").exists()) + assertEquals("deep content", destination.resolve("level1/level2/deep.txt").readText()) + } + + @Test + fun `should extract symlink from assets tar gz`() { + val extractDir = tempDir.resolve("extracted") + val zipFile = getFileFromResources("archive/single_symlink.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + extractTarGz(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + val symlink = extractDir.resolve("link.txt") + assertTrue(symlink.exists()) + assertTrue(symlink.isSymbolicLink()) + } + + @Test + fun `should extract empty folder from assets tar gz`() { + val extractDir = tempDir.resolve("extracted") + val zipFile = getFileFromResources("archive/empty_folder.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + extractTarGz(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + assertTrue(extractDir.resolve("archive").exists()) + } + + + @Test + fun `should handle mixed content in tar gz`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + + // Regular files + sourceDir.resolve("text.txt").writeText("text") + sourceDir.resolve("binary.bin").writeBytes(ByteArray(100) { it.toByte() }) + + // Directories + val nestedDir = sourceDir.resolve("nested/dir").createDirectories() + nestedDir.resolve("nested.txt").writeText("nested") + + val target = sourceDir.resolve("target.txt").apply { writeText("target") } + sourceDir.resolve("link.txt").createSymbolicLinkPointingTo(target) + + val outputFile = tempDir.resolve("output.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // When + tarGz(sourceDir, outputFile, withTopLevelFolder = false, tmpDir, logger) + + // Then + val extractDir = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + extractTarGz(outputFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + assertTrue(extractDir.resolve("text.txt").exists()) + assertTrue(extractDir.resolve("binary.bin").exists()) + assertTrue(extractDir.resolve("nested/dir/nested.txt").exists()) + assertEquals("text", extractDir.resolve("text.txt").readText()) + assertEquals("nested", extractDir.resolve("nested/dir/nested.txt").readText()) + val symlink = extractDir.resolve("link.txt") + assertTrue(symlink.exists()) + assertTrue(symlink.isSymbolicLink()) + } + + @Test + fun `should handle mixed content in assets tar gz`() { + // Given + val archiveFile = getFileFromResources("archive/mixed.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + // Then + val extractDir = tempDir.resolve("extracted") + extractTarGz(archiveFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + assertTrue(extractDir.resolve("text.txt").exists()) + assertTrue(extractDir.resolve("binary.bin").exists()) + assertTrue(extractDir.resolve("nested/nested.txt").exists()) + assertEquals("abc", extractDir.resolve("text.txt").readText().trim()) + assertEquals("nested", extractDir.resolve("nested/nested.txt").readText().trim()) + val symlink = extractDir.resolve("link.txt") + assertTrue(symlink.exists()) + assertTrue(symlink.isSymbolicLink()) + } + // ========== ExtractTarZst Tests ========== + + @Test + fun `should extract tar zst without stripping top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file.txt").writeText("zst content") + val tarZst = tempDir.resolve("archive.tar.zst") + val tmpDir = tempDir.resolve("tmp1") + tarZst(sourceDir, tarZst, withTopLevelFolder = true, tmpDir, logger) + + val destination = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + extractTarZst(tarZst, destination, stripTopLevelFolder = false, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(destination.resolve("source/file.txt").exists()) + assertEquals("zst content", destination.resolve("source/file.txt").readText()) + } + + @Test + fun `should extract tar zst with stripping top level folder`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file.txt").writeText("zst content") + val tarZst = tempDir.resolve("archive.tar.zst") + val tmpDir = tempDir.resolve("tmp1") + tarZst(sourceDir, tarZst, withTopLevelFolder = true, tmpDir, logger) + + val destination = tempDir.resolve("extracted") + val extractTmpDir = tempDir.resolve("tmp2") + + // When + extractTarZst(tarZst, destination, stripTopLevelFolder = true, cleanDestination = false, extractTmpDir, logger) + + // Then + assertTrue(destination.resolve("file.txt").exists()) + assertEquals("zst content", destination.resolve("file.txt").readText()) + } + + // ========== Edge Cases and Error Handling ========== + + @Test + fun `should handle large files`() { + // Given + val sourceFile = tempDir.resolve("large.txt") + val largeContent = "x".repeat(1024 * 1024) // 1MB + sourceFile.writeText(largeContent) + + val gzFile = tempDir.resolve("large.gz") + + // When + gz(gzFile, sourceFile) + + // Then + assertTrue(gzFile.exists()) + assertTrue(gzFile.fileSize() > 0) + assertTrue(gzFile.fileSize() < largeContent.length) // Should be compressed + } + + @Test + fun `should overwrite existing archive`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + sourceDir.resolve("file1.txt").writeText("content1") + val targetZip = tempDir.resolve("output.zip") + + // Create first archive + zip(targetZip, sourceDir, withTopLevelFolder = false) + val firstSize = targetZip.fileSize() + + // Modify source + sourceDir.resolve("file2.txt").writeText("content2") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + val secondSize = targetZip.fileSize() + assertTrue(secondSize > firstSize) + val entries = readZipEntryNames(targetZip) + assertEquals(2, entries.size) + } + + @Test + fun `should handle deeply nested directory structures`() { + // Given + val sourceDir = tempDir.resolve("source").createDirectories() + val deepDir = sourceDir.resolve("a/b/c/d/e/f/g/h").createDirectories() + deepDir.resolve("deep.txt").writeText("deep") + val targetZip = tempDir.resolve("output.zip") + + // When + zip(targetZip, sourceDir, withTopLevelFolder = false) + + // Then + val destination = tempDir.resolve("extracted") + extractZip(targetZip, destination, stripTopLevelFolder = false, cleanDestination = false, temporaryDir = tempDir.resolve("tmp"), logger = logger) + assertTrue(destination.resolve("a/b/c/d/e/f/g/h/deep.txt").exists()) + assertEquals("deep", destination.resolve("a/b/c/d/e/f/g/h/deep.txt").readText()) + } + + @Test + fun `should handle empty sequence for zip`() { + // Given + val emptySequence = emptySequence>() + val targetZip = tempDir.resolve("empty.zip") + + // When + zip(targetZip, emptySequence) + + // Then + assertTrue(targetZip.exists()) + val entries = readZipEntryNames(targetZip) + assertEquals(0, entries.size) + } + + @Test + fun `tarGz and extractTarGz should preserve file permissions`() { + assumePosixFileSystem() + + // Given + val sourceDir = tempDir.resolve("src").createDirectories() + val file = sourceDir.resolve("script.sh").apply { writeText("echo hi") } + val expectedPerms = setOf( + PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, + PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, + PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, + PosixFilePermission.OTHERS_EXECUTE, + ) + + file.setPosixFilePermissions(expectedPerms) + + val archive = tempDir.resolve("perm.tar.gz") + val tmp = tempDir.resolve("tmp") + tarGz(sourceDir, archive, withTopLevelFolder = false, tmp, logger) + + val dest = tempDir.resolve("out-tar") + extractTarGz(archive, dest, stripTopLevelFolder = false, cleanDestination = false, tmp, logger) + + // Then + val extracted = dest.resolve("script.sh") + assertTrue(extracted.exists()) + val actualPerms = extracted.getPosixFilePermissions() + assertEquals(expectedPerms, actualPerms) + } + + @Test + fun `zip and extractZip should preserve file permissions`() { + assumePosixFileSystem() + + // Given + val sourceDir = tempDir.resolve("srcZip").createDirectories() + val file = sourceDir.resolve("script.sh").apply { writeText("echo hi") } + val expectedPerms = setOf( + PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, + PosixFilePermission.OWNER_EXECUTE, + PosixFilePermission.GROUP_READ, + PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, + PosixFilePermission.OTHERS_EXECUTE, + ) + + file.setPosixFilePermissions(expectedPerms) + + val archive = tempDir.resolve("script.sh") + val tmp = tempDir.resolve("tmpZip") + zip(sourceDir, archive, withTopLevelFolder = false, tmp, logger) + + val dest = tempDir.resolve("out-zip") + extractZip(archive, dest, stripTopLevelFolder = false, cleanDestination = false, tmp, logger) + + // Then + val extracted = dest.resolve("script.sh") + assertTrue(extracted.exists()) + val actualPerms = extracted.getPosixFilePermissions() + assertEquals(expectedPerms, actualPerms) + } + + @Test + fun `extractZip should preserve file permissions from assets`() { + assumePosixFileSystem() + + val extractDir = tempDir.resolve("extracted") + val zipFile = getFileFromResources("archive/readonly.zip") + val tmpDir = tempDir.resolve("tmp") + + val expectedPermissions = setOf( + PosixFilePermission.OWNER_READ, + PosixFilePermission.GROUP_READ, + PosixFilePermission.OTHERS_READ, + ) + + extractZip(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + val readonlyFile = extractDir.resolve("readonly.txt") + assertTrue(readonlyFile.exists()) + val actualPerms = readonlyFile.getPosixFilePermissions() + assertEquals(expectedPermissions, actualPerms) + } + + @Test + fun `extractTarGz should preserve file permissions from assets`() { + assumePosixFileSystem() + + val extractDir = tempDir.resolve("extracted") + val zipFile = getFileFromResources("archive/readonly.tar.gz") + val tmpDir = tempDir.resolve("tmp") + + val expectedPermissions = setOf( + PosixFilePermission.OWNER_READ, + PosixFilePermission.GROUP_READ, + PosixFilePermission.OTHERS_READ, + ) + + extractTarGz(zipFile, extractDir, stripTopLevelFolder = false, cleanDestination = false, tmpDir, logger) + + val readonlyFile = extractDir.resolve("readonly.txt") + assertTrue(readonlyFile.exists()) + val actualPerms = readonlyFile.getPosixFilePermissions() + assertEquals(expectedPermissions, actualPerms) + } + + // ========== Helper Functions ========== + + private fun readZipEntries(zipFile: Path): Map { + val result = mutableMapOf() + ZipInputStream(zipFile.inputStream()).use { zipIn -> + var entry = zipIn.nextEntry + while (entry != null) { + if (!entry.isDirectory) { + result[entry.name] = zipIn.readBytes().decodeToString() + } + zipIn.closeEntry() + entry = zipIn.nextEntry + } + } + return result + } + + private fun readZipEntryNames(zipFile: Path): List { + val result = mutableListOf() + ZipInputStream(zipFile.inputStream()).use { zipIn -> + var entry = zipIn.nextEntry + while (entry != null) { + if (!entry.isDirectory) { + result.add(entry.name) + } + zipIn.closeEntry() + entry = zipIn.nextEntry + } + } + return result + } + + private fun createTestZip(zipFile: Path, files: Map) { + ZipOutputStream(zipFile.outputStream()).use { zipOut -> + files.forEach { (name, content) -> + zipOut.putNextEntry(ZipEntry(name)) + zipOut.write(content.toByteArray()) + zipOut.closeEntry() + } + } + } + + private fun getFileFromResources(path: String): Path { + return Paths.get(javaClass.classLoader.getResource(path)!!.toURI()) + } + + + private fun assumePosixFileSystem() { + assumeTrue("Posix permissions are not supported on this system. Skipping test.", tempDir.isPosixSupported()) + } + + private fun Path.isPosixSupported(): Boolean { + return try { + Files.getFileStore(this).supportsFileAttributeView("posix") + } + catch (_: IOException) { + // Could not determine if a file system supports POSIX attributes, assume it does not + false + } + } +} \ No newline at end of file diff --git a/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt new file mode 100644 index 000000000000..942752efed02 --- /dev/null +++ b/fleet/build/fs/srcJvmTest/fleet/buildtool/fs/UtilsTest.kt @@ -0,0 +1,48 @@ +package fleet.buildtool.fs + +import java.io.File +import java.nio.ByteBuffer +import kotlin.io.path.readBytes +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals + +class UtilsTest { + @Test + fun `sha256 should not be modified as it will break dockApiVersion generation`() { + assertEquals( + "3202d841030f0606d93ea9e89aa06261a784615c961c94bfd92286d30538e8c8", + sha256(resourceAsFile("/example-abi-lf.txt").toPath().readBytesForSha256()) + ) + } + + @Test + fun `sha256 should be agnostic of line endings`() { + val expectedHash = "3202d841030f0606d93ea9e89aa06261a784615c961c94bfd92286d30538e8c8" + assertEquals(expectedHash, sha256(resourceAsFile("/example-abi-lf.txt").toPath().readBytesForSha256()), "LF line ending file must produce correct hash") + assertEquals(expectedHash, sha256(resourceAsFile("/example-abi-crlf.txt").toPath().readBytesForSha256()), "CRLF line ending file must produce correct hash") + assertEquals(expectedHash, sha256(resourceAsFile("/example-abi-cr.txt").toPath().readBytesForSha256()), "CR line ending file must produce correct hash") + } + + @Test + fun `should have no action on LF`() { + val lfFile = resourceAsFile("/example-abi-lf.txt").toPath().readBytes() + assertContentEquals(lfFile, ByteBuffer.wrap(lfFile).normaliseLineSeparators().array(), "should not have modified the content") + } + + @Test + fun `should normalize CRLF`() { + val lfFile = resourceAsFile("/example-abi-lf.txt").toPath().readBytes() + assertContentEquals(lfFile, ByteBuffer.wrap(resourceAsFile("/example-abi-crlf.txt").toPath().readBytes()).normaliseLineSeparators().array(), "should not contain LF") + } + + @Test + fun `should normalize CR `() { + val lfFile = resourceAsFile("/example-abi-lf.txt").toPath().readBytes() + assertContentEquals(lfFile, ByteBuffer.wrap(resourceAsFile("/example-abi-cr.txt").toPath().readBytes()).normaliseLineSeparators().array(), "should not contain CR") + } +} + +private fun resourceAsFile(filename: String) = object {}.javaClass.getResource(filename)?.let { File(it.toURI()) } ?: error( + "could not find test resource $filename" +) \ No newline at end of file diff --git a/fleet/build/http/BUILD.bazel b/fleet/build/http/BUILD.bazel new file mode 100644 index 000000000000..b1a0eaa35042 --- /dev/null +++ b/fleet/build/http/BUILD.bazel @@ -0,0 +1,69 @@ +### auto-generated section `build fleet.build.http` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_http", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "http", + module_name = "fleet.build.http", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_http", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-coroutines-core", + "//fleet/ktor/client/core", + "//fleet/build/fs", + "@lib//:fleet-io-ktor-client-logging", + "@lib//:slf4j-api", + "@lib//:fleet-io-ktor-client-mock", + ], + exports = [ + "@lib//:kotlinx-coroutines-core", + "//fleet/ktor/client/core", + ] +) + +jvm_library( + name = "http_test_lib", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmTest/**/*.kt", "srcJvmTest/**/*.java", "srcJvmTest/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_http", + associates = [":http"], + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-coroutines-core", + "//fleet/ktor/client/core", + "//fleet/build/fs", + "//fleet/build/fs:fs_test_lib", + "@lib//:fleet-io-ktor-client-logging", + "@lib//:slf4j-api", + "//fleet/junit4", + "@lib//:kotlin-test", + "@lib//:fleet-io-ktor-client-mock", + ], + exports = [ + "@lib//:kotlinx-coroutines-core", + "//fleet/ktor/client/core", + ] +) +### auto-generated section `build fleet.build.http` end + +### auto-generated section `test fleet.build.http` start +load("@community//build:tests-options.bzl", "jps_test") + +jps_test( + name = "http_test", + runtime_deps = [":http_test_lib"] +) +### auto-generated section `test fleet.build.http` end \ No newline at end of file diff --git a/fleet/build/http/fleet.build.http.iml b/fleet/build/http/fleet.build.http.iml new file mode 100644 index 000000000000..a1da9435717c --- /dev/null +++ b/fleet/build/http/fleet.build.http.iml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/http/gradlebuild/build.gradle.kts b/fleet/build/http/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..6a650b00b6b6 --- /dev/null +++ b/fleet/build/http/gradlebuild/build.gradle.kts @@ -0,0 +1,76 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.http" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + api(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) { + isTransitive = false + } + implementation(jps.io.ktor.ktor.client.logging.jvm1780199912.get().let { "${it.group}:ktor-client-logging:${it.version}" }) { + isTransitive = false + } + implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + exclude(group = "org.slf4j", module = "slf4j-jdk14") + } + implementation(jps.io.ktor.ktor.client.mock.jvm121671911.get().let { "${it.group}:ktor-client-mock:${it.version}" }) { + isTransitive = false + } + api(project(":fleet.ktor.client.core")) + implementation(project(":fleet.build.fs")) + } + sourceSets.commonTest.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.test542871666.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + } + } + sourceSets.jvmTest.dependencies { + implementation(project(":fleet.junit4")) + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt b/fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt new file mode 100644 index 000000000000..ca92611a37aa --- /dev/null +++ b/fleet/build/http/srcJvmMain/fleet/buildtool/http/HttpClient.kt @@ -0,0 +1,177 @@ +package fleet.buildtool.http + +import fleet.buildtool.fs.* +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.logging.* +import io.ktor.client.plugins.logging.Logger as KtorLogger +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.utils.io.* +import io.ktor.utils.io.jvm.javaio.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import java.time.Duration +import org.slf4j.Logger +import kotlin.io.path.* + +fun fleetBuildHttpClient(logger: Logger, userAgent: String = "FleetBuildTooling/1.0"): HttpClient = HttpClient { + expectSuccess = true + followRedirects = true + install(HttpTimeout) { + requestTimeoutMillis = Duration.ofMinutes(5).toMillis() + } + install(HttpRequestRetry) { + retryOnServerErrors(maxRetries = 10) + exponentialDelay() + } + install(Logging) { + this.logger = object : KtorLogger { + override fun log(message: String) { + logger.info(message) + } + } + level = LogLevel.ALL + sanitizeHeader { header -> header == HttpHeaders.Authorization } + } + defaultRequest { + header("User-Agent", userAgent) + } +} + +fun HttpClient.downloadFileBlocking(url: String, destination: Path, checksumValidation: ChecksumValidation = ChecksumValidation.Disabled, block: HttpRequestBuilder.() -> Unit = {}): Path = runBlocking { + downloadFile(url, destination, checksumValidation, block) +} + +sealed class ChecksumValidation { + /** + * Download the file without checking its integrity once on disk. + * + * Always re-downloads the file, regardless of whether the target file is already present on disk. + */ + object Disabled : ChecksumValidation() + + /** + * Checks integrity of the downloaded file. + * + * Checks whether the target file is already on disk and matching the provided [expected] hash and if the hash matches the file won't be re-downloaded. + * + * @param algorithm of the [expected] hash string + * @param expected the expected hash of the downloaded file + */ + data class UsingHash(val algorithm: ChecksumAlgorithm, val expected: String) : ChecksumValidation() + + /** + * Checks integrity of the downloaded file by downloading first the file pointed by [url] which should contain the hash of the downloaded + * file. + * + * Checks whether the target file is already on disk and matching the hash contained in the file downloaded from [url] and if the hash + * matches the file won't be re-downloaded. + * + * @param algorithm of the hash present in the file downloaded from [url] + * @param url url to a remote checksum file to download + */ + data class UsingHashDownloadedFile(val algorithm: ChecksumAlgorithm, val url: String) : ChecksumValidation() +} + +enum class ChecksumAlgorithm { + SHA256, +} + +suspend fun HttpClient.downloadFile(url: String, destination: Path, checksumValidation: ChecksumValidation, block: HttpRequestBuilder.() -> Unit = {}): Path { + return when (val hashResult = checkIfAlreadyDownloaded(destination, checksumValidation)) { + is HashResult.AlreadyDownloaded -> destination + HashResult.NotDownloaded, is HashResult.HashMismatched -> { + config { + install(Logging) { + level = LogLevel.HEADERS // a bug in Ktor Logging plugin leads to OOM if downloaded file is too big + } + }.prepareGet(url) { + expectSuccess = false + block() + }.execute { response -> + if (!response.status.isSuccess()) { + withContext(Dispatchers.IO) { Files.deleteIfExists(destination) } + error("failed to download $url: ${response.status} ${response.bodyAsText()}") + } + + val channel: ByteReadChannel = response.body() + withContext(Dispatchers.IO) { + destination.parent.createDirectories() + destination.outputStream(StandardOpenOption.CREATE).buffered().use { fos -> + channel.copyTo(fos) + } + } + } + + when (checksumValidation) { + ChecksumValidation.Disabled -> destination // no integrity validation + is ChecksumValidation.UsingHash, + is ChecksumValidation.UsingHashDownloadedFile, + -> { + val expected = when (hashResult) { + HashResult.NotDownloaded -> resolveChecksumHash(checksumValidation) ?: error("impossible to get an `null` hash here") + is HashResult.HashMismatched -> hashResult.expectedHash // do not re-resolve the expected hash, instead use the result of the previous hash check that happened at checkIfAlreadyDownloaded + is HashResult.AlreadyDownloaded -> error("impossible") // would have hit first when branch + } + val actual = sha256(destination.readBytes()) + when (expected == actual) { + true -> destination + else -> error("downloaded file '$destination' failed integrity check, expected=$expected, actual=$actual") + } + } + } + } + } +} + +private suspend fun HttpClient.resolveChecksumHash(checksumValidation: ChecksumValidation): String? = when (checksumValidation) { + ChecksumValidation.Disabled -> null + is ChecksumValidation.UsingHashDownloadedFile -> { + require(checksumValidation.algorithm == ChecksumAlgorithm.SHA256) { "Only SHA-256 algorithm is supported for now" } + val tmp = createTempFile(prefix = "hash", suffix = ".sha256") + val hashFile = downloadFile(checksumValidation.url, tmp, ChecksumValidation.Disabled) + val hash = hashFile.readText().split(" ", limit = 2).first() + tmp.deleteIfExists() + hash + } + is ChecksumValidation.UsingHash -> { + require(checksumValidation.algorithm == ChecksumAlgorithm.SHA256) { "Only SHA-256 algorithm is supported for now" } + checksumValidation.expected + } +} + +private suspend fun HttpClient.checkIfAlreadyDownloaded(destination: Path, checksumValidation: ChecksumValidation): HashResult = when (destination.exists()) { + false -> HashResult.NotDownloaded + true -> when (checksumValidation) { + ChecksumValidation.Disabled -> { + destination.deleteIfExists() + HashResult.NotDownloaded + } + is ChecksumValidation.UsingHashDownloadedFile, + is ChecksumValidation.UsingHash, + -> { + val expected = resolveChecksumHash(checksumValidation) ?: error("impossible to get an `null` hash here") + val actual = sha256(destination.readBytes()) + when (expected == actual) { + true -> HashResult.AlreadyDownloaded(hash = actual) + else -> { + withContext(Dispatchers.IO) { destination.deleteIfExists() } + HashResult.HashMismatched(expectedHash = expected) + } + } + } + } +} + +private sealed class HashResult { + data class AlreadyDownloaded(val hash: String) : HashResult() + object NotDownloaded : HashResult() + data class HashMismatched(val expectedHash: String) : HashResult() +} diff --git a/fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt b/fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt new file mode 100644 index 000000000000..6a421dd4216d --- /dev/null +++ b/fleet/build/http/srcJvmTest/fleet/buildtool/http/HttpClientTests.kt @@ -0,0 +1,183 @@ +package fleet.buildtool.http + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf +import io.ktor.utils.io.ByteReadChannel +import java.util.concurrent.atomic.AtomicInteger +import kotlin.io.path.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class HttpClientTests { + @OptIn(ExperimentalPathApi::class) + @Test + fun `should always download file when checksum validation is disabled`() { + val resourceUrl = "https://example.com/some-resource.txt" + val requestCounter = mapOf( + resourceUrl to AtomicInteger(0), + ) + + val mockEngine = MockEngine { request -> + requestCounter.getValue(request.url.toString()).incrementAndGet() + respond(content = ByteReadChannel("sometestdata"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt")) + } + + val httpClient = HttpClient(mockEngine) {} + val destination = createTempDirectory().resolve("some-resource.txt") + destination.parent.createDirectories() + + requestCounter.getValue(resourceUrl).set(0) + destination.deleteIfExists() + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.Disabled) + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file") + assertEquals("sometestdata", destination.readText()) + + requestCounter.getValue(resourceUrl).set(0) + destination.parent.createDirectories() + destination.writeText("some other data but that will be overridden anyway") + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.Disabled) + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file") + assertEquals("sometestdata", destination.readText()) + + requestCounter.getValue(resourceUrl).set(0) + destination.parent.createDirectories() + destination.writeText("sometestdata") + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.Disabled) + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file despite it already existing and matching checksum, because checksum validation is disabled") + assertEquals("sometestdata", destination.readText()) + + destination.parent.deleteRecursively() + } + + @OptIn(ExperimentalPathApi::class) + @Test + fun `should not re-downloaded file if already on disk and matching the hash resolve by URL`() { + val resourceUrl = "https://example.com/some-resource.txt" + val resourceUrlSha = "$resourceUrl.sha256" + val requestCounter = mapOf( + resourceUrl to AtomicInteger(0), + resourceUrlSha to AtomicInteger(0), + ) + + val mockEngine = MockEngine { request -> + requestCounter.getValue(request.url.toString()).incrementAndGet() + when (request.url.toString()) { + resourceUrl -> respond(content = ByteReadChannel("sometestdata"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt")) + resourceUrlSha -> respond(content = ByteReadChannel("10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486 *some-resource.txt"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt")) + else -> error("unexpected request") + } + } + + val httpClient = HttpClient(mockEngine) {} + val destination = createTempDirectory().resolve("some-resource.txt") + destination.parent.createDirectories() + + requestCounter.getValue(resourceUrl).set(0) + requestCounter.getValue(resourceUrlSha).set(0) + destination.deleteIfExists() + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHashDownloadedFile(ChecksumAlgorithm.SHA256, resourceUrlSha)) + assertEquals(1, requestCounter.getValue(resourceUrlSha).get(), "should have tried to download the checksum file once for integrity check") + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it did not exist") + assertEquals("sometestdata", destination.readText()) + + requestCounter.getValue(resourceUrl).set(0) + requestCounter.getValue(resourceUrlSha).set(0) + destination.parent.createDirectories() + destination.writeText("some other data but that will be overridden anyway") + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHashDownloadedFile(ChecksumAlgorithm.SHA256, resourceUrlSha)) + assertEquals(1, requestCounter.getValue(resourceUrlSha).get(), "should have tried to download the checksum file once for integrity check, but not twice because it re-used the first download of the checksum file") + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it existed but did not matched checksum") + assertEquals("sometestdata", destination.readText()) + + requestCounter.getValue(resourceUrl).set(0) + requestCounter.getValue(resourceUrlSha).set(0) + destination.parent.createDirectories() + destination.writeText("sometestdata") + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHashDownloadedFile(ChecksumAlgorithm.SHA256, resourceUrlSha)) + assertEquals(1, requestCounter.getValue(resourceUrlSha).get(), "should have tried to download the checksum file once for integrity check, but not twice because it re-used the first download of the checksum file") + assertEquals(0, requestCounter.getValue(resourceUrl).get(), "should not have tried to download the file because it exists and matched checksum") + assertEquals("sometestdata", destination.readText()) + + destination.parent.deleteRecursively() + } + + @OptIn(ExperimentalPathApi::class) + @Test + fun `should not re-downloaded file if already on disk and matching the raw hash`() { + val resourceUrl = "https://example.com/some-resource.txt" + val requestCounter = mapOf( + resourceUrl to AtomicInteger(0), + ) + + val mockEngine = MockEngine { request -> + requestCounter.getValue(request.url.toString()).incrementAndGet() + when (request.url.toString()) { + resourceUrl -> respond(content = ByteReadChannel("sometestdata"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt")) + else -> error("unexpected request") + } + } + + val httpClient = HttpClient(mockEngine) {} + val destination = createTempDirectory().resolve("some-resource.txt") + destination.parent.createDirectories() + + requestCounter.getValue(resourceUrl).set(0) + destination.deleteIfExists() + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486")) + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it did not exist") + assertEquals("sometestdata", destination.readText()) + + requestCounter.getValue(resourceUrl).set(0) + destination.parent.createDirectories() + destination.writeText("some other data but that will be overridden anyway") + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486")) + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it existed but did not matched checksum") + assertEquals("sometestdata", destination.readText()) + + requestCounter.getValue(resourceUrl).set(0) + destination.parent.createDirectories() + destination.writeText("sometestdata") + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486")) + assertEquals(0, requestCounter.getValue(resourceUrl).get(), "should not have tried to download the file because it exists and matched checksum") + assertEquals("sometestdata", destination.readText()) + + destination.parent.deleteRecursively() + } + + @OptIn(ExperimentalPathApi::class) + @Test + fun `should fail when server returned a file not matching checksum`() { + val resourceUrl = "https://example.com/some-resource.txt" + val requestCounter = mapOf( + resourceUrl to AtomicInteger(0), + ) + + val mockEngine = MockEngine { request -> + requestCounter.getValue(request.url.toString()).incrementAndGet() + when (request.url.toString()) { + resourceUrl -> respond(content = ByteReadChannel("sometestdataDJFBSIDFUBSDIFUSDBFIUDB"), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/txt")) + else -> error("unexpected request") + } + } + + val httpClient = HttpClient(mockEngine) {} + val destination = createTempDirectory().resolve("some-resource.txt") + destination.parent.createDirectories() + + requestCounter.getValue(resourceUrl).set(0) + destination.deleteIfExists() + val exception = assertFailsWith { + httpClient.downloadFileBlocking(resourceUrl, destination, ChecksumValidation.UsingHash(ChecksumAlgorithm.SHA256, "10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486")) + } + assertEquals("downloaded file '$destination' failed integrity check, expected=10ba28f2395f43928e1fcc3dd482dc2f1b6a9cef7334d1afda11369f2a1a5486, actual=5d2080c90b2d11ff9f17adb960c0f579931d94311e2c47cf5c2d97de4865d4f0", exception.message) + assertEquals(1, requestCounter.getValue(resourceUrl).get(), "should have tried to download the file because it did not exist") + assertEquals("sometestdataDJFBSIDFUBSDIFUSDBFIUDB", destination.readText()) + + destination.parent.deleteRecursively() + } +} diff --git a/fleet/build/license/BUILD.bazel b/fleet/build/license/BUILD.bazel new file mode 100644 index 000000000000..34f6ce457f21 --- /dev/null +++ b/fleet/build/license/BUILD.bazel @@ -0,0 +1,28 @@ +### auto-generated section `build fleet.build.license` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_license", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "license", + module_name = "fleet.build.license", + visibility = ["//visibility:public"], + srcs = glob(["srcCommonMain/**/*.kt", "srcCommonMain/**/*.java", "srcCommonMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_license", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-serialization-core", + "@lib//:kotlinx-serialization-json", + ] +) +### auto-generated section `build fleet.build.license` end \ No newline at end of file diff --git a/fleet/build/license/fleet.build.license.iml b/fleet/build/license/fleet.build.license.iml new file mode 100644 index 000000000000..6cbd1f6fb05b --- /dev/null +++ b/fleet/build/license/fleet.build.license.iml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/license/gradlebuild/build.gradle.kts b/fleet/build/license/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..d0397b043313 --- /dev/null +++ b/fleet/build/license/gradlebuild/build.gradle.kts @@ -0,0 +1,59 @@ +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + alias(jps.plugins.kotlin.serialization) + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.license" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.core.jvm1739247612.get().let { "${it.group}:kotlinx-serialization-core:${it.version}" }) { + isTransitive = false + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.json.jvm231489733.get().let { "${it.group}:kotlinx-serialization-json:${it.version}" }) { + isTransitive = false + } + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/license/srcCommonMain/.pseudoCommonKotlinSourceSet b/fleet/build/license/srcCommonMain/.pseudoCommonKotlinSourceSet new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt b/fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt new file mode 100644 index 000000000000..2cba291fdd16 --- /dev/null +++ b/fleet/build/license/srcCommonMain/fleet/buildtool/license/LicenseUtils.kt @@ -0,0 +1,18 @@ +package fleet.buildtool.license + +import kotlinx.serialization.Serializable + +fun thirdPartyLicenseFilename( + productName: String, + version: String, + extension: String, +): String = "$productName-$version-third-party-libraries${extension}" + +@Serializable +data class JetbrainsLicenceEntry( + val name: String, + val version: String?, + val url: String, + val license: String?, + val licenseUrl: String?, +) diff --git a/fleet/build/platform/BUILD.bazel b/fleet/build/platform/BUILD.bazel new file mode 100644 index 000000000000..0bfcd2d1829d --- /dev/null +++ b/fleet/build/platform/BUILD.bazel @@ -0,0 +1,29 @@ +### auto-generated section `build fleet.build.platform` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_platform", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "platform", + module_name = "fleet.build.platform", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_platform", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:kotlinx-serialization-core", + "@lib//:kotlinx-serialization-json", + "@lib//:kotlinx-coroutines-core", + ] +) +### auto-generated section `build fleet.build.platform` end \ No newline at end of file diff --git a/fleet/build/platform/fleet.build.platform.iml b/fleet/build/platform/fleet.build.platform.iml new file mode 100644 index 000000000000..eaca7177a849 --- /dev/null +++ b/fleet/build/platform/fleet.build.platform.iml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + $KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/platform/gradlebuild/build.gradle.kts b/fleet/build/platform/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..a7b14599d9bd --- /dev/null +++ b/fleet/build/platform/gradlebuild/build.gradle.kts @@ -0,0 +1,63 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + alias(jps.plugins.kotlin.serialization) + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.platform" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.core.jvm1739247612.get().let { "${it.group}:kotlinx-serialization-core:${it.version}" }) { + isTransitive = false + } + implementation(jps.org.jetbrains.kotlinx.kotlinx.serialization.json.jvm231489733.get().let { "${it.group}:kotlinx-serialization-json:${it.version}" }) { + isTransitive = false + } + implementation(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) { + isTransitive = false + } + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt new file mode 100644 index 000000000000..b709c2e88d36 --- /dev/null +++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/DistributionChannel.kt @@ -0,0 +1,32 @@ +package fleet.buildtool.platform + +enum class DistributionChannel { + EAP, EAPCandidate, Nightly, Unspecified; + + companion object { + fun fromString(s: String?): DistributionChannel = when (s) { + "nightly" -> Nightly + "stable", + "eap", + -> EAP + "eap_candidate" -> EAPCandidate + "", null -> Unspecified + else -> error("unsupported channel string '$s'") + } + } +} + +fun DistributionChannel.toMarketplaceChannel(): MarketplaceChannel = when (this) { + DistributionChannel.EAP, + DistributionChannel.EAPCandidate, + -> MarketplaceChannel.Stable + DistributionChannel.Nightly -> MarketplaceChannel.Nightly + DistributionChannel.Unspecified -> MarketplaceChannel.Nightly // defaults to Nightly when channel of Fleet is unspecified +} + +fun DistributionChannel.toFeedChannel(): String = when (this) { + DistributionChannel.EAP -> "eap" + DistributionChannel.EAPCandidate -> "eap_candidate" + DistributionChannel.Nightly -> "nightly" + DistributionChannel.Unspecified -> error("cannot resolve feed channel from an unspecified distribution channel") +} diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt new file mode 100644 index 000000000000..73f9a796d63b --- /dev/null +++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/MarketplaceChannel.kt @@ -0,0 +1,15 @@ +package fleet.buildtool.platform + +// TODO: maybe this needs its own module instead + +enum class MarketplaceChannel(val value: String) { + Stable("stable"), + Nightly("nightly"); + + override fun toString(): String = value + + companion object { + private val byValues = MarketplaceChannel.values().associateBy { it.value } + fun fromString(s: String): MarketplaceChannel? = byValues[s] + } +} diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt new file mode 100644 index 000000000000..dcbb9c6b40ad --- /dev/null +++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/Platform.kt @@ -0,0 +1,196 @@ +package fleet.buildtool.platform + +import kotlinx.serialization.Serializable +import java.nio.file.Path +import java.io.Serializable as JavaSerializable + +val supportedPlatforms = listOf( + Platform.Windows.WindowsX64, + Platform.Windows.WindowsAarch64, + Platform.Linux.LinuxX64, + Platform.Linux.LinuxAarch64, + Platform.MacOs.MacOsX64, + Platform.MacOs.MacOsAarch64, +) +val supportedOses = supportedPlatforms.map { it.os }.toSet() + +@Serializable +sealed class Platform(val os: OS) : JavaSerializable { + abstract val arch: Arch + + @Serializable + sealed class Windows(override val arch: Arch) : Platform(OS.WINDOWS) { + @Serializable + object WindowsX64 : Windows(Arch.X64) { + private fun readResolve(): Any = WindowsX64 + } + + @Serializable + object WindowsAarch64 : Windows(Arch.AARCH64) { + private fun readResolve(): Any = WindowsAarch64 + } + } + + @Serializable + sealed class Linux(override val arch: Arch) : Platform(OS.LINUX) { + @Serializable + object LinuxX64 : Linux(Arch.X64) { + private fun readResolve(): Any = LinuxX64 + } + + @Serializable + object LinuxAarch64 : Linux(Arch.AARCH64) { + private fun readResolve(): Any = LinuxAarch64 + } + } + + @Serializable + sealed class MacOs(override val arch: Arch) : Platform(OS.MACOS) { + @Serializable + object MacOsX64 : MacOs(Arch.X64) { + private fun readResolve(): Any = MacOsX64 + } + + @Serializable + object MacOsAarch64 : MacOs(Arch.AARCH64) { + private fun readResolve(): Any = MacOsAarch64 + } + } + + companion object { + fun buildPlatform(): Platform { + val os = OS.currentOs() + val arch = Arch.currentArch() + return supportedPlatforms.findPlatform(os, arch) + } + + fun windows(arch: Arch) = when (arch) { + Arch.AARCH64 -> Windows.WindowsAarch64 + Arch.X64 -> Windows.WindowsX64 + } + + fun linux(arch: Arch) = when (arch) { + Arch.AARCH64 -> Linux.LinuxAarch64 + Arch.X64 -> Linux.LinuxX64 + } + + fun macos(arch: Arch) = when (arch) { + Arch.AARCH64 -> MacOs.MacOsAarch64 + Arch.X64 -> MacOs.MacOsX64 + } + + fun List.findPlatform(os: OS, arch: Arch) = singleOrNull { p -> + p.os == os && p.arch == arch + } ?: error("unsupported platform $os $arch (or more than one supported platform is matching this triple)") + } +} + +enum class Arch { + AARCH64, X64; + + companion object { + fun fromString(arch: String): Arch = when (arch.lowercase()) { + "x86_64", "amd64", "x64" -> X64 + "arm64", "aarch64" -> AARCH64 + else -> error("unsupported arch '$arch'") + } + + fun currentArch(): Arch { + val arch = System.getProperty("os.arch") ?: error("failed to resolve system property 'os.arch'") + return fromString(arch) + } + } +} + +enum class OS { + MACOS, WINDOWS, LINUX; + + companion object { + fun fromString(os: String): OS = with(os.lowercase()) { + when { + contains("win") -> WINDOWS + contains("mac") -> MACOS + contains("nix") || contains("nux") -> LINUX + else -> error("unsuppported OS '$os'") + } + } + + fun currentOs(): OS { + val os = System.getProperty("os.name") ?: error("failed to resolve system property 'os.name'") + return fromString(os) + } + } +} + +enum class LibCImplementation { + GLIBC, MUSL; + + companion object { + fun fromString(libc: String): LibCImplementation = when (libc.lowercase()) { + "musl" -> MUSL + "glibc" -> GLIBC + else -> error("unsupported libc implementation '$libc'") + } + + // TODO: proper heuristic on libc detection for alpine linux support for example + fun currentLibc() = GLIBC + } +} + +/** + * String representation of OS in Fleet parts (S3 storage, artefact names, etc.) + */ +fun OS.toFleetPartOsString() = when (this) { + OS.MACOS -> "macos" + OS.WINDOWS -> "windows" + OS.LINUX -> "linux" +} + +/** + * String representation of Arch in Fleet parts (S3 storage, artefact names, etc.) + */ +fun Arch.toFleetPartArchString() = when (this) { + Arch.AARCH64 -> "aarch64" + Arch.X64 -> "x64" +} + +fun Arch.distributionArchSuffix() = when (this) { + Arch.AARCH64 -> "-aarch64" + Arch.X64 -> "" +} + +/** + * String representation of lib C implementation in Fleet parts (S3 storage, Gradle tooling, etc.) + */ +fun LibCImplementation.toFleetPartLibcString() = when (this) { + LibCImplementation.GLIBC -> null // we do not specify GLIBC in the distribution slug, as of legacy + LibCImplementation.MUSL -> "musl" +} + +/** + * Converts FleetPlatform to the distribution slug used in our S3 for Fleet parts + * + * Beware, changing this code might result to a massive breaking change for customers of Fleet. + */ +fun Platform.toS3DistributionSlug(withLibC: LibCImplementation? = null): String { + val osName = os.toFleetPartOsString() + val archName = arch.toFleetPartArchString() + val libc = withLibC?.toFleetPartLibcString() + + return when { + libc != null -> "${osName}_${libc}-$archName" + else -> "${osName}_$archName" + } +} + +fun Path.withPlatformBinaryExtension(platform: Platform): Path = when (platform) { + is Platform.Windows -> resolveSibling("${fileName}.exe") + else -> this +} + +fun Platform.binaryExtension() = when (this) { + is Platform.Windows -> ".exe" + else -> "" +} + +fun String.withPlatformBinaryExtension(platform: Platform): String = "$this${platform.binaryExtension()}" diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt new file mode 100644 index 000000000000..c8c5ead4d273 --- /dev/null +++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/RustTarget.kt @@ -0,0 +1,28 @@ +package fleet.buildtool.platform + +import java.io.Serializable + +data class RustTarget(val platform: Platform, val libCImplementation: LibCImplementation? = null) : Serializable { + + override fun toString(): String = when (platform) { + Platform.Linux.LinuxAarch64 -> when (libCImplementation) { + LibCImplementation.GLIBC -> "aarch64-unknown-linux-gnu" + LibCImplementation.MUSL, null -> "aarch64-unknown-linux-musl" + } + + Platform.Linux.LinuxX64 -> when (libCImplementation) { + LibCImplementation.GLIBC -> "x86_64-unknown-linux-gnu" + LibCImplementation.MUSL, null -> "x86_64-unknown-linux-musl" + } + + Platform.MacOs.MacOsAarch64 -> "aarch64-apple-darwin" + Platform.MacOs.MacOsX64 -> "x86_64-apple-darwin" + Platform.Windows.WindowsAarch64 -> "aarch64-pc-windows-msvc" + Platform.Windows.WindowsX64 -> "x86_64-pc-windows-msvc" + } +} + +fun Platform.toFleetRustTarget(overrideLibCImplementation: LibCImplementation? = null): RustTarget = + RustTarget(this, overrideLibCImplementation) + +fun RustTarget.toS3DistributionSlug(): String = platform.toS3DistributionSlug(libCImplementation) diff --git a/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt new file mode 100644 index 000000000000..a07e615b3a58 --- /dev/null +++ b/fleet/build/platform/srcJvmMain/fleet/buildtool/platform/TeamcityUtils.kt @@ -0,0 +1,5 @@ +package fleet.buildtool.platform + +const val TEAMCITY_VERSION = "TEAMCITY_VERSION" + +fun isRunningOnTeamcity(): Boolean = System.getenv(TEAMCITY_VERSION)?.isNotBlank() ?: false diff --git a/fleet/build/rules/fleet_module.bzl b/fleet/build/rules/fleet_module.bzl new file mode 100644 index 000000000000..df99050e62fb --- /dev/null +++ b/fleet/build/rules/fleet_module.bzl @@ -0,0 +1,31 @@ +visibility("private") + +def _fleet_module(ctx): + return [ + DefaultInfo( + files = depset([]), + ), + ] + +fleet_module = rule( + attrs = { + "module_name": attr.string( + doc = """The Fleet module name""", + ), + "module_library": attr.label( + providers = [ + [JavaInfo], + ], + allow_files = False, + ), + "_haven_cli": attr.label( + default = "//fleet/build/cli:haven", + executable = True, + # allow_single_file = True, + cfg = "exec", + # cfg = scrubbed_host_platform_transition, + ), + }, + provides = [], + implementation = _fleet_module, +) diff --git a/fleet/build/s3/BUILD.bazel b/fleet/build/s3/BUILD.bazel new file mode 100644 index 000000000000..e20a464fa77c --- /dev/null +++ b/fleet/build/s3/BUILD.bazel @@ -0,0 +1,30 @@ +### auto-generated section `build fleet.build.s3` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_s3", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "s3", + module_name = "fleet.build.s3", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_s3", + deps = [ + "@lib//:kotlin-stdlib", + "//fleet/build/platform", + "//fleet/build/fs", + "@lib//:kotlinx-coroutines-core", + "@lib//:fleet-aws-sdk-kotlin-s3-jvm", + ] +) +### auto-generated section `build fleet.build.s3` end \ No newline at end of file diff --git a/fleet/build/s3/fleet.build.s3.iml b/fleet/build/s3/fleet.build.s3.iml new file mode 100644 index 000000000000..e40cd28fd132 --- /dev/null +++ b/fleet/build/s3/fleet.build.s3.iml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/s3/gradlebuild/build.gradle.kts b/fleet/build/s3/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..063f1a296e6c --- /dev/null +++ b/fleet/build/s3/gradlebuild/build.gradle.kts @@ -0,0 +1,65 @@ +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.s3" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.com.intellij.platform.kotlinx.coroutines.core.jvm134738847.get().let { "${it.group}:kotlinx-coroutines-core:${it.version}" }) { + isTransitive = false + } + implementation(jps.aws.sdk.kotlin.s3.jvm321782429.get().let { "${it.group}:s3:${it.version}" }) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib") + exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-jvm") + exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core") + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8") + exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common") + exclude(group = "org.slf4j", module = "slf4j-api") + } + implementation(project(":fleet.build.platform")) + implementation(project(":fleet.build.fs")) + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt new file mode 100644 index 000000000000..5223f95c70ac --- /dev/null +++ b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Keys.kt @@ -0,0 +1,62 @@ +package fleet.buildtool.s3 + +import fleet.buildtool.platform.* + +sealed class S3Version { + abstract override fun toString(): String + + class Specific(val version: String) : S3Version() { + override fun toString() = version + } + + /** + * Used for local testing only, artefacts using this version should never be used in production code. + * This is a mitigation until we have some of the logic described in https://youtrack.jetbrains.com/issue/FL-18599 + */ + object Latest : S3Version() { + override fun toString() = "latest" + } +} + +private const val partsPrefix = "fleet-parts" + +fun Platform.fleetFleetDockS3Key(s3Version: S3Version): String = fleetPartS3Key(s3Version, "fleet-dock", { ".tar.zst" }) +fun Platform.fleetFleetJBRS3Key(s3Version: S3Version): String = fleetPartS3Key(s3Version, "fleet-jbr", { ".tar.zst" }, archiveName = "jbr") + +fun Platform.fleetPartS3Key( + s3Version: S3Version, + partName: String, + extension: () -> String, + archiveName: String = partName, +) = fleetPartS3Key( + s3Version = s3Version, + partName = partName, + extension = extension, + distributionSlug = "${toS3DistributionSlug()}/", + archiveName = archiveName, +) + +fun fleetPartS3Key( + s3Version: S3Version, + partName: String, + extension: () -> String, + archiveName: String = partName, +): String = fleetPartS3Key( + s3Version, + partName, + extension, + distributionSlug = "", + archiveName = archiveName, +) + +fun fleetPartS3Key( + s3Version: S3Version, + partName: String, + extension: () -> String, + distributionSlug: String, + archiveName: String = partName, +): String = when (s3Version) { + S3Version.Latest -> "fleet-parts/latest/$partName/$distributionSlug$archiveName-latest${extension()}" + is S3Version.Specific -> "$partsPrefix/$partName/$distributionSlug$archiveName-${s3Version.version}${extension()}" +} + diff --git a/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt new file mode 100644 index 000000000000..2ab26010d1ab --- /dev/null +++ b/fleet/build/s3/srcJvmMain/fleet/buildtool/s3/S3Utils.kt @@ -0,0 +1,20 @@ +package fleet.buildtool.s3 + +import aws.sdk.kotlin.services.s3.S3Client +import aws.sdk.kotlin.services.s3.headObject +import aws.sdk.kotlin.services.s3.model.HeadObjectRequest +import aws.sdk.kotlin.services.s3.model.NotFound +import aws.sdk.kotlin.services.s3.model.S3Exception + +suspend fun S3Client.objectExists(block: HeadObjectRequest.Builder.() -> Unit): Boolean = try { + headObject(block) + true +} +catch (e: S3Exception) { + when { + e is NotFound -> false // object does not exist + e.sdkErrorMetadata.errorCode == "404" -> false // object does not exist + e.sdkErrorMetadata.errorCode == "403" -> false // object does not exist but credentials lacks `s3:ListBucket` permission + else -> throw e + } +} diff --git a/fleet/build/sign/BUILD.bazel b/fleet/build/sign/BUILD.bazel new file mode 100644 index 000000000000..b8d0a636d954 --- /dev/null +++ b/fleet/build/sign/BUILD.bazel @@ -0,0 +1,27 @@ +### auto-generated section `build fleet.build.sign` start +load("//build:compiler-options.bzl", "create_kotlinc_options") +load("@rules_jvm//:jvm.bzl", "jvm_library") + +create_kotlinc_options( + name = "custom_sign", + opt_in = [ + "kotlinx.coroutines.ExperimentalCoroutinesApi", + "kotlin.ExperimentalStdlibApi", + ], + x_consistent_data_class_copy_visibility = True, + x_jvm_default = "all-compatibility", + x_lambdas = "class" +) + +jvm_library( + name = "sign", + module_name = "fleet.build.sign", + visibility = ["//visibility:public"], + srcs = glob(["srcJvmMain/**/*.kt", "srcJvmMain/**/*.java", "srcJvmMain/**/*.form"], allow_empty = True, exclude = ["**/module-info.java"]), + kotlinc_opts = ":custom_sign", + deps = [ + "@lib//:kotlin-stdlib", + "@lib//:slf4j-api", + ] +) +### auto-generated section `build fleet.build.sign` end \ No newline at end of file diff --git a/fleet/build/sign/fleet.build.sign.iml b/fleet/build/sign/fleet.build.sign.iml new file mode 100644 index 000000000000..35018d5e1dad --- /dev/null +++ b/fleet/build/sign/fleet.build.sign.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fleet/build/sign/gradlebuild/build.gradle.kts b/fleet/build/sign/gradlebuild/build.gradle.kts new file mode 100644 index 000000000000..fc3d6d1bd271 --- /dev/null +++ b/fleet/build/sign/gradlebuild/build.gradle.kts @@ -0,0 +1,56 @@ +// IMPORT__MARKER_START +import fleet.buildtool.conventions.configureAtMostOneJvmTargetOrThrow +import fleet.buildtool.conventions.withJavaSourceSet +// IMPORT__MARKER_END + +plugins { + alias(libs.plugins.kotlin.multiplatform) + id("fleet.project-module-conventions") + id("fleet.toolchain-conventions") + alias(libs.plugins.dokka) + id("fleet.module-publishing-conventions") + id("fleet.sdk-repositories-publishing-conventions") + // GRADLE_PLUGINS__MARKER_START + id("fleet-module") + // GRADLE_PLUGINS__MARKER_END +} + +fleetModule { + module { + name = "fleet.build.sign" + importedFromJps {} + } +} + +@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +kotlin { + // KOTLIN__MARKER_START + compilerOptions.freeCompilerArgs = listOf( + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlin.ExperimentalStdlibApi", + "-Xlambdas=class", + "-Xconsistent-data-class-copy-visibility", + "-XXLanguage:+AllowEagerSupertypeAccessibilityChecks", + ) + jvm {} + sourceSets.commonMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonMain")) } + sourceSets.commonMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonMain")) } + sourceSets.commonTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcCommonTest")) } + sourceSets.commonTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesCommonTest")) } + sourceSets.jvmMain.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("main") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmMain")) } } } + sourceSets.jvmMain.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmMain")) } + sourceSets.jvmTest.configure { kotlin.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } + configureAtMostOneJvmTargetOrThrow { compilations.named("test") { withJavaSourceSet { javaSourceSet -> javaSourceSet.java.srcDir(layout.projectDirectory.dir("../srcJvmTest")) } } } + sourceSets.jvmTest.configure { resources.srcDir(layout.projectDirectory.dir("../resourcesJvmTest")) } + sourceSets.commonMain.dependencies { + implementation(jps.org.jetbrains.kotlin.kotlin.stdlib1993400674.get().let { "${it.group}:${it.name}:${it.version}" }) { + exclude(group = "org.jetbrains", module = "annotations") + } + implementation(jps.org.slf4j.slf4j.api2013636515.get().let { "${it.group}:${it.name}:${it.version}" }) { + isTransitive = false + exclude(group = "org.slf4j", module = "slf4j-jdk14") + } + } + // KOTLIN__MARKER_END +} \ No newline at end of file diff --git a/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt new file mode 100644 index 000000000000..32dbb14a4de4 --- /dev/null +++ b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/FleetSigner.kt @@ -0,0 +1,11 @@ +package fleet.buildtool.sign + +import org.slf4j.Logger +import java.nio.file.Path + +val jetSignJsonContentType: Pair = "contentType" to "text/plain" + +interface FleetSigner { + fun signFiles(files: Collection, options: Map = emptyMap(), logger: Logger) + fun gpgSign(data: Map, options: Map = emptyMap(), temporaryDirectory: Path, logger: Logger): Map +} diff --git a/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt new file mode 100644 index 000000000000..2fc63d4e84cd --- /dev/null +++ b/fleet/build/sign/srcJvmMain/fleet/buildtool/sign/MockSigner.kt @@ -0,0 +1,23 @@ +package fleet.buildtool.sign + +import org.slf4j.Logger +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import kotlin.io.path.pathString +import java.io.Serializable as JavaSerializable + +object MockSigner : FleetSigner, JavaSerializable { + override fun signFiles(files: Collection, options: Map, logger: Logger) { + files.forEach { file -> + Files.writeString(file.resolveSibling("${file.fileName.pathString}.asc"), "", StandardOpenOption.CREATE) + logger.info("[mockSign]: pretended to sign '${file.fileName.pathString}'") + } + } + + override fun gpgSign(data: Map, options: Map, temporaryDirectory: Path, logger: Logger): Map = + data.mapValues { (key, _) -> + logger.info("[mockSign]: pretended to gpg sign '$key'") + byteArrayOf() + } +} diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel index ee0b02b5f0bd..2f3eebd1146b 100644 --- a/lib/BUILD.bazel +++ b/lib/BUILD.bazel @@ -1458,6 +1458,300 @@ jvm_import( visibility = ["//visibility:public"] ) +java_library( + name = "fleet-aws-sdk-kotlin-s3-jvm", + exports = [ + ":aws_sdk_kotlin-s3-jvm-1_0_69_http_import", + ":aws_sdk_kotlin-aws-config-jvm-1_0_69_http_import", + ":aws_smithy_kotlin-serde-json-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http_import", + ":aws_sdk_kotlin-aws-core-jvm-1_0_69_http_import", + ":aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http_import", + ":aws_smithy_kotlin-http-client-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-runtime-core-jvm-1_0_16_http_import", + ":com_squareup_okio-okio-jvm-3_6_0_http_import", + ":aws_smithy_kotlin-smithy-client-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http_import", + ":aws_sdk_kotlin-aws-http-jvm-1_0_69_http_import", + ":aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-http-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-http-auth-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http_import", + ":com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http_import", + ":com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http_import", + ":aws_smithy_kotlin-identity-api-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-serde-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-serde-xml-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http_import", + ":aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http_import", + ], + visibility = ["//visibility:public"] +) + +jvm_import( + name = "aws_sdk_kotlin-s3-jvm-1_0_69_http_import", + jar = "@aws_sdk_kotlin-s3-jvm-1_0_69_http//file", + source_jar = "@aws_sdk_kotlin-s3-jvm-1_0_69_http//file" +) + +jvm_import( + name = "aws_sdk_kotlin-aws-config-jvm-1_0_69_http_import", + jar = "@aws_sdk_kotlin-aws-config-jvm-1_0_69_http//file", + source_jar = "@aws_sdk_kotlin-aws-config-jvm-1_0_69_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-serde-json-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-serde-json-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-serde-json-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_sdk_kotlin-aws-core-jvm-1_0_69_http_import", + jar = "@aws_sdk_kotlin-aws-core-jvm-1_0_69_http//file", + source_jar = "@aws_sdk_kotlin-aws-core-jvm-1_0_69_http//file" +) + +jvm_import( + name = "aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http_import", + jar = "@aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http//file", + source_jar = "@aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-client-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-client-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-client-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-runtime-core-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-runtime-core-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-runtime-core-jvm-1_0_16_http//file" +) + +jvm_import( + name = "com_squareup_okio-okio-jvm-3_6_0_http_import", + jar = "@com_squareup_okio-okio-jvm-3_6_0_http//file", + source_jar = "@com_squareup_okio-okio-jvm-3_6_0_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-smithy-client-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-smithy-client-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-smithy-client-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_sdk_kotlin-aws-http-jvm-1_0_69_http_import", + jar = "@aws_sdk_kotlin-aws-http-jvm-1_0_69_http//file", + source_jar = "@aws_sdk_kotlin-aws-http-jvm-1_0_69_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-auth-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-auth-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-auth-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http//file" +) + +jvm_import( + name = "com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http_import", + jar = "@com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http//file", + source_jar = "@com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http//file" +) + +jvm_import( + name = "com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http_import", + jar = "@com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http//file", + source_jar = "@com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-identity-api-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-identity-api-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-identity-api-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-serde-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-serde-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-serde-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-serde-xml-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-serde-xml-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-serde-xml-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http//file" +) + +jvm_import( + name = "aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http_import", + jar = "@aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http//file", + source_jar = "@aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http//file" +) + +java_library( + name = "fleet-clikt", + exports = [ + ":com_github_ajalt_clikt-clikt-jvm-5_0_3_http_import", + ":com_github_ajalt_mordant-mordant-jvm-3_0_1_http_import", + ":com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http_import", + ":com_github_ajalt_colormath-colormath-jvm-3_6_0_http_import", + ], + visibility = ["//visibility:public"] +) + +jvm_import( + name = "com_github_ajalt_clikt-clikt-jvm-5_0_3_http_import", + jar = "@com_github_ajalt_clikt-clikt-jvm-5_0_3_http//file", + source_jar = "@com_github_ajalt_clikt-clikt-jvm-5_0_3_http//file" +) + +jvm_import( + name = "com_github_ajalt_mordant-mordant-jvm-3_0_1_http_import", + jar = "@com_github_ajalt_mordant-mordant-jvm-3_0_1_http//file", + source_jar = "@com_github_ajalt_mordant-mordant-jvm-3_0_1_http//file" +) + +jvm_import( + name = "com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http_import", + jar = "@com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http//file", + source_jar = "@com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http//file" +) + +jvm_import( + name = "com_github_ajalt_colormath-colormath-jvm-3_6_0_http_import", + jar = "@com_github_ajalt_colormath-colormath-jvm-3_6_0_http//file", + source_jar = "@com_github_ajalt_colormath-colormath-jvm-3_6_0_http//file" +) + +jvm_import( + name = "fleet-clikt-core", + jar = "@com_github_ajalt_clikt-clikt-core-jvm-5_0_3_http//file", + source_jar = "@com_github_ajalt_clikt-clikt-core-jvm-5_0_3_http//file", + visibility = ["//visibility:public"] +) + +jvm_import( + name = "fleet-github-luben-zstd-jni", + jar = "@com_github_luben-zstd-jni-1_5_5-11_http//file", + source_jar = "@com_github_luben-zstd-jni-1_5_5-11_http//file", + visibility = ["//visibility:public"] +) + +jvm_import( + name = "fleet-io-github-pdvrieze-xmlutil-core-jvm", + jar = "@io_github_pdvrieze_xmlutil-core-jvm-0_86_3_http//file", + source_jar = "@io_github_pdvrieze_xmlutil-core-jvm-0_86_3_http//file", + visibility = ["//visibility:public"] +) + +jvm_import( + name = "fleet-io-github-pdvrieze-xmlutil-serialization-jvm", + jar = "@io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3_http//file", + source_jar = "@io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3_http//file", + visibility = ["//visibility:public"] +) + jvm_import( name = "fleet-io-ktor-client-cio", jar = "@io_ktor-ktor-client-cio-jvm-3_1_3_http//file", @@ -1472,6 +1766,20 @@ jvm_import( visibility = ["//visibility:public"] ) +jvm_import( + name = "fleet-io-ktor-client-logging", + jar = "@io_ktor-ktor-client-logging-jvm-3_1_3_http//file", + source_jar = "@io_ktor-ktor-client-logging-jvm-3_1_3_http//file", + visibility = ["//visibility:public"] +) + +jvm_import( + name = "fleet-io-ktor-client-mock", + jar = "@io_ktor-ktor-client-mock-jvm-3_1_3_http//file", + source_jar = "@io_ktor-ktor-client-mock-jvm-3_1_3_http//file", + visibility = ["//visibility:public"] +) + jvm_import( name = "fleet-io-ktor-events", jar = "@io_ktor-ktor-events-jvm-3_1_3_http//file", @@ -1570,6 +1878,55 @@ jvm_import( visibility = ["//visibility:public"] ) +java_library( + name = "fleet-jetbrains-intellij-idea-community-build-zip", + exports = [ + ":com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http_import", + ":io_netty-netty-buffer-4_2_0_RC2_http_import", + ":io_netty-netty-common-4_2_0_RC2_http_import", + ":org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http_import", + ":com_dynatrace_hash4j-hash4j-0_20_0_http_import", + ], + visibility = ["//visibility:public"] +) + +jvm_import( + name = "com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http_import", + jar = "@com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http//file", + source_jar = "@com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http//file" +) + +jvm_import( + name = "io_netty-netty-buffer-4_2_0_RC2_http_import", + jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file", + source_jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file" +) + +jvm_import( + name = "io_netty-netty-common-4_2_0_RC2_http_import", + jar = "@io_netty-netty-common-4_2_0_RC2_http//file", + source_jar = "@io_netty-netty-common-4_2_0_RC2_http//file" +) + +jvm_import( + name = "org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http_import", + jar = "@org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http//file", + source_jar = "@org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http//file" +) + +jvm_import( + name = "com_dynatrace_hash4j-hash4j-0_20_0_http_import", + jar = "@com_dynatrace_hash4j-hash4j-0_20_0_http//file", + source_jar = "@com_dynatrace_hash4j-hash4j-0_20_0_http//file" +) + +jvm_import( + name = "fleet-jetbrains-intellij-platform-util-zip-squashed", + jar = "@com_jetbrains_intellij_platform-util-zip-squashed-252_16432_http//file", + source_jar = "@com_jetbrains_intellij_platform-util-zip-squashed-252_16432_http//file", + visibility = ["//visibility:public"] +) + jvm_import( name = "fleet-org-fusesource-jansi", jar = "@org_fusesource_jansi-jansi-2_4_1_http//file", @@ -1577,6 +1934,13 @@ jvm_import( visibility = ["//visibility:public"] ) +jvm_import( + name = "fleet-tukaani-xz", + jar = "@org_tukaani-xz-1_9_http//file", + source_jar = "@org_tukaani-xz-1_9_http//file", + visibility = ["//visibility:public"] +) + jvm_import( name = "fleet-util-modules-jetbrains-intellij-platform-util-zip-squashed", jar = "@com_jetbrains_intellij_platform-util-zip-squashed-252_16432_http//file", @@ -8069,18 +8433,6 @@ java_library( visibility = ["//visibility:public"] ) -jvm_import( - name = "io_netty-netty-buffer-4_2_0_RC2_http_import", - jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file", - source_jar = "@io_netty-netty-buffer-4_2_0_RC2_http//file" -) - -jvm_import( - name = "io_netty-netty-common-4_2_0_RC2_http_import", - jar = "@io_netty-netty-common-4_2_0_RC2_http//file", - source_jar = "@io_netty-netty-common-4_2_0_RC2_http//file" -) - jvm_import( name = "netty-codec-compression", jar = "@io_netty-netty-codec-compression-4_2_0_RC2_http//file", diff --git a/lib/MODULE.bazel b/lib/MODULE.bazel index 78d5bc139398..729c460690db 100644 --- a/lib/MODULE.bazel +++ b/lib/MODULE.bazel @@ -1784,6 +1784,566 @@ http_file( downloaded_file_path = "file-prediction-model-0.3.0-sources.jar" ) +http_file( + name = "aws_sdk_kotlin-s3-jvm-1_0_69_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/s3-jvm/1.0.69/s3-jvm-1.0.69.jar", + sha256 = "3d6cd0bf48c65e965903fa4720d713badeeaa5433bd7b278075c484e3a5709a8", + downloaded_file_path = "s3-jvm-1.0.69.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-config-jvm-1_0_69_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-config-jvm/1.0.69/aws-config-jvm-1.0.69.jar", + sha256 = "877ffeaa6fc96192da7383b3c3e16717cb52504f488b80937d796eb875e5565a", + downloaded_file_path = "aws-config-jvm-1.0.69.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-json-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-json-jvm/1.0.16/serde-json-jvm-1.0.16.jar", + sha256 = "2f12ed8fbd53e91cb3b2a163b5d06ae2c39f538bd74f23a678889f5be02e8675", + downloaded_file_path = "serde-json-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-form-url-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-form-url-jvm/1.0.16/serde-form-url-jvm-1.0.16.jar", + sha256 = "198b9b1ec6c33da8c460e6a2e0859d1308f1fcb3f7d4138100f7dd75207edd80", + downloaded_file_path = "serde-form-url-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-json-protocols-jvm/1.0.16/aws-json-protocols-jvm-1.0.16.jar", + sha256 = "633ccbae483c99b27e311a6a70157cfc0dfa19f8eb3ba263198f40f87266c09c", + downloaded_file_path = "aws-json-protocols-jvm-1.0.16.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-core-jvm-1_0_69_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-core-jvm/1.0.69/aws-core-jvm-1.0.69.jar", + sha256 = "e738ebef7be523c4e1940f5e5b2390ba853d4333f547038b2b31fb3964e99933", + downloaded_file_path = "aws-core-jvm-1.0.69.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-endpoint-jvm-1_0_69_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-endpoint-jvm/1.0.69/aws-endpoint-jvm-1.0.69.jar", + sha256 = "d3f30ef5e3b11b358f28f42422226585b72e4a11bf13b1fc3411962dcfde64f3", + downloaded_file_path = "aws-endpoint-jvm-1.0.69.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-client-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-jvm/1.0.16/http-client-jvm-1.0.16.jar", + sha256 = "aa584dcd05862a8bbe7eb7db73959d93ca6b527cc44536b7d10fcda733411d4b", + downloaded_file_path = "http-client-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-runtime-core-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/runtime-core-jvm/1.0.16/runtime-core-jvm-1.0.16.jar", + sha256 = "8a2323e7d08ff07d2f869e4bcae834773ce0a0e8704cd7404c9625992a748662", + downloaded_file_path = "runtime-core-jvm-1.0.16.jar" +) + +http_file( + name = "com_squareup_okio-okio-jvm-3_6_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okio/okio-jvm/3.6.0/okio-jvm-3.6.0.jar", + sha256 = "67543f0736fc422ae927ed0e504b98bc5e269fda0d3500579337cb713da28412", + downloaded_file_path = "okio-jvm-3.6.0.jar" +) + +http_file( + name = "aws_smithy_kotlin-smithy-client-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/smithy-client-jvm/1.0.16/smithy-client-jvm-1.0.16.jar", + sha256 = "ce382d40f998d8dd11008967c3bf4cd51c47182512b84b5f1d48e48d2a3f9770", + downloaded_file_path = "smithy-client-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-telemetry-api-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-api-jvm/1.0.16/telemetry-api-jvm-1.0.16.jar", + sha256 = "508bde368d4b174402aeeccec68c7e29ad9349a953b1efab75ebeaa6c0a21f0e", + downloaded_file_path = "telemetry-api-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-credentials-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-credentials-jvm/1.0.16/aws-credentials-jvm-1.0.16.jar", + sha256 = "01489854c4f9494de6cd5446df8e3c694ad3f6582c5369b9578eb18754f1720c", + downloaded_file_path = "aws-credentials-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-event-stream-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-event-stream-jvm/1.0.16/aws-event-stream-jvm-1.0.16.jar", + sha256 = "71fe60ba15b5f9c43f5ee1da0edc13fd98f2a85dbf8c9dcafc666bc1c7e668e7", + downloaded_file_path = "aws-event-stream-jvm-1.0.16.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-http-jvm-1_0_69_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-http-jvm/1.0.69/aws-http-jvm-1.0.69.jar", + sha256 = "6b7584f233e0715f5394edfbfd5536a480113efe99f3d49bfd58d2395072177a", + downloaded_file_path = "aws-http-jvm-1.0.69.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-protocol-core-jvm/1.0.16/aws-protocol-core-jvm-1.0.16.jar", + sha256 = "6d7028c6e695b872e0ba91679af4dbbabf8e1bfbd6f7fb52be30bb1e398d595d", + downloaded_file_path = "aws-protocol-core-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-signing-common-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-common-jvm/1.0.16/aws-signing-common-jvm-1.0.16.jar", + sha256 = "30cf05f9f7ec7d6676031ee2b0c8f4b33b107a93a179ca931d57baa983de70a3", + downloaded_file_path = "aws-signing-common-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-signing-default-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-default-jvm/1.0.16/aws-signing-default-jvm-1.0.16.jar", + sha256 = "dd0616302ce4a3fc1c03326bad37e5cf3509377692b6aca5ea17d581ad582d62", + downloaded_file_path = "aws-signing-default-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-xml-protocols-jvm/1.0.16/aws-xml-protocols-jvm-1.0.16.jar", + sha256 = "94cc5ebcb2d819ebb8c628f64ffa352426f23b3b422834bfa8199cf497a1365d", + downloaded_file_path = "aws-xml-protocols-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-jvm/1.0.16/http-jvm-1.0.16.jar", + sha256 = "3f0d5d4e67a3097b56b32fa7b26589da9d6bd0968693b395ad1c8f49f60dbdfd", + downloaded_file_path = "http-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-auth-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-jvm/1.0.16/http-auth-jvm-1.0.16.jar", + sha256 = "79ed729b7e134609310de7d691b1b80870f467a95bc84f2f6bbf3e43ae83de85", + downloaded_file_path = "http-auth-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-auth-api-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-api-jvm/1.0.16/http-auth-api-jvm-1.0.16.jar", + sha256 = "9664c8d36468c1f575b356f04025ce17417e584a0bb19b640b115b10a8dce72c", + downloaded_file_path = "http-auth-api-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-auth-aws-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-aws-jvm/1.0.16/http-auth-aws-jvm-1.0.16.jar", + sha256 = "31e3811fd85ba7fcba38a4067d63af8c13c1e05ae7062639fd8c954520315f51", + downloaded_file_path = "http-auth-aws-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-default-jvm/1.0.16/http-client-engine-default-jvm-1.0.16.jar", + sha256 = "bbbf92cbc8df78a8fa20f0b35613e8d41024ee6525b538ebda280917e1d7e812", + downloaded_file_path = "http-client-engine-default-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-okhttp-jvm/1.0.16/http-client-engine-okhttp-jvm-1.0.16.jar", + sha256 = "4bfb91b4c6373ac2b1d94a085c69935ca1b56b7b27b21c5228c5bc5da6f7e2dc", + downloaded_file_path = "http-client-engine-okhttp-jvm-1.0.16.jar" +) + +http_file( + name = "com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-jvm/5.0.0-alpha.11/okhttp-jvm-5.0.0-alpha.11.jar", + sha256 = "506a1d3aba9f99b55570c7f8813cd79a44946531f29050d5b938474efdb4cf80", + downloaded_file_path = "okhttp-jvm-5.0.0-alpha.11.jar" +) + +http_file( + name = "com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-coroutines-jvm/5.0.0-alpha.11/okhttp-coroutines-jvm-5.0.0-alpha.11.jar", + sha256 = "1452873e0cc9637b7e1a717f2cca0cbccebd00c05aaaf67b8adb06777bf4f461", + downloaded_file_path = "okhttp-coroutines-jvm-5.0.0-alpha.11.jar" +) + +http_file( + name = "aws_smithy_kotlin-identity-api-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/identity-api-jvm/1.0.16/identity-api-jvm-1.0.16.jar", + sha256 = "be3f9bd5ebfb83aa353f407a4389f7b3454a6945f82cea1d41e370aeedae71cd", + downloaded_file_path = "identity-api-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-jvm/1.0.16/serde-jvm-1.0.16.jar", + sha256 = "c017ee899656cf5abf22c3793603cb33ae35f0a805b9fa0707f8146a73a14a7f", + downloaded_file_path = "serde-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-xml-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-xml-jvm/1.0.16/serde-xml-jvm-1.0.16.jar", + sha256 = "72c8559a241af92ce122c0df0dc3749d638d616ebd5da3ba3749a95abca3b349", + downloaded_file_path = "serde-xml-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-defaults-jvm/1.0.16/telemetry-defaults-jvm-1.0.16.jar", + sha256 = "4343154bb1b0a282d86b627932a465722eeda3cccd6cc4986fc5e5c4013bfef4", + downloaded_file_path = "telemetry-defaults-jvm-1.0.16.jar" +) + +http_file( + name = "aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/logging-slf4j2-jvm/1.0.16/logging-slf4j2-jvm-1.0.16.jar", + sha256 = "66c0700ca0c27046b0b12e6cf4c4da423800b88c1c72b2709cd5cbc3e0f1eb64", + downloaded_file_path = "logging-slf4j2-jvm-1.0.16.jar" +) + +http_file( + name = "aws_sdk_kotlin-s3-jvm-1_0_69-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/s3-jvm/1.0.69/s3-jvm-1.0.69-sources.jar", + sha256 = "c7df05b60aec94b092cff2959f1c19d649c169c79c59146c14c69ab6edd451b8", + downloaded_file_path = "s3-jvm-1.0.69-sources.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-config-jvm-1_0_69-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-config-jvm/1.0.69/aws-config-jvm-1.0.69-sources.jar", + sha256 = "1d4ab3c4eb73f800a059d65f7e7983969a1fbccc2ebc02f999aa41f54fdb314b", + downloaded_file_path = "aws-config-jvm-1.0.69-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-json-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-json-jvm/1.0.16/serde-json-jvm-1.0.16-sources.jar", + sha256 = "63312de11976e37ccd4a8ccc51c6db0a51220d548d8d25e2d568b2cd36d7b5b7", + downloaded_file_path = "serde-json-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-form-url-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-form-url-jvm/1.0.16/serde-form-url-jvm-1.0.16-sources.jar", + sha256 = "2fa020f36f3e20da2445a4fe9a7081da9fb9c5099781c9110de5b62bbe291422", + downloaded_file_path = "serde-form-url-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-json-protocols-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-json-protocols-jvm/1.0.16/aws-json-protocols-jvm-1.0.16-sources.jar", + sha256 = "82b5eb3de2edbc38c42558490b612b2c5c2e69ad47183a812c26f0423a6f7f6f", + downloaded_file_path = "aws-json-protocols-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-core-jvm-1_0_69-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-core-jvm/1.0.69/aws-core-jvm-1.0.69-sources.jar", + sha256 = "2a0fd31b290758e74e725b2d42b7c071cfa458aed45aee2a77ec8601cb434cca", + downloaded_file_path = "aws-core-jvm-1.0.69-sources.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-endpoint-jvm-1_0_69-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-endpoint-jvm/1.0.69/aws-endpoint-jvm-1.0.69-sources.jar", + sha256 = "1bf9476e19cf4f20f4ede1bb004a819825dbda051c6ac00b486720570b8b77e1", + downloaded_file_path = "aws-endpoint-jvm-1.0.69-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-client-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-jvm/1.0.16/http-client-jvm-1.0.16-sources.jar", + sha256 = "1a21fe3e336c009cb1334993eab2c0cc97c0412775df55a139c8a11c9ca7afcd", + downloaded_file_path = "http-client-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-runtime-core-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/runtime-core-jvm/1.0.16/runtime-core-jvm-1.0.16-sources.jar", + sha256 = "6db2e152b1b92fc66c381add6e3c378cdcc1bef41cc0c1d8eee78fecd01bba16", + downloaded_file_path = "runtime-core-jvm-1.0.16-sources.jar" +) + +http_file( + name = "com_squareup_okio-okio-jvm-3_6_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okio/okio-jvm/3.6.0/okio-jvm-3.6.0-sources.jar", + sha256 = "870a42a7b468af8fd24d67b7b62e5861aab398c56f15a80b10642e7001564e2f", + downloaded_file_path = "okio-jvm-3.6.0-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-smithy-client-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/smithy-client-jvm/1.0.16/smithy-client-jvm-1.0.16-sources.jar", + sha256 = "4f326ea673b6cd99b33739e6fb4df8ae80bd28def81cc3e1641f33c44c4d6f54", + downloaded_file_path = "smithy-client-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-telemetry-api-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-api-jvm/1.0.16/telemetry-api-jvm-1.0.16-sources.jar", + sha256 = "b5a2a420df517652ada3da3de5534e836d78633ae528d9b6aa3fd32b0bad4668", + downloaded_file_path = "telemetry-api-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-credentials-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-credentials-jvm/1.0.16/aws-credentials-jvm-1.0.16-sources.jar", + sha256 = "ec3a34bfb8b38e620233d7bf54e66e87e22644f74d884635aa2ed942442ea945", + downloaded_file_path = "aws-credentials-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-event-stream-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-event-stream-jvm/1.0.16/aws-event-stream-jvm-1.0.16-sources.jar", + sha256 = "2b732ea574cab4c437f717873193aeade7482ed7e6f024d1a368549cf8cb0ae1", + downloaded_file_path = "aws-event-stream-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_sdk_kotlin-aws-http-jvm-1_0_69-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/sdk/kotlin/aws-http-jvm/1.0.69/aws-http-jvm-1.0.69-sources.jar", + sha256 = "1c6858e4f5e036a118e5cf54d7b72990a968252141e65da489955cec0103919b", + downloaded_file_path = "aws-http-jvm-1.0.69-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-protocol-core-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-protocol-core-jvm/1.0.16/aws-protocol-core-jvm-1.0.16-sources.jar", + sha256 = "db32fffaf7bda060bde5623ac91da87888af287629c5a44b34026f86a4ed7a59", + downloaded_file_path = "aws-protocol-core-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-signing-common-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-common-jvm/1.0.16/aws-signing-common-jvm-1.0.16-sources.jar", + sha256 = "0e43d7624b1d6f9aa0700f4cbed81951002f53a5138d412e84aa373eb57750b5", + downloaded_file_path = "aws-signing-common-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-signing-default-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-signing-default-jvm/1.0.16/aws-signing-default-jvm-1.0.16-sources.jar", + sha256 = "d8c2708cf555ce2925aa60a51540bf1480440e8f991bcedefd62fd6f07c48d23", + downloaded_file_path = "aws-signing-default-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-aws-xml-protocols-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/aws-xml-protocols-jvm/1.0.16/aws-xml-protocols-jvm-1.0.16-sources.jar", + sha256 = "249a38bd14fae9257d1424311e94aaf01c552cee4bf3d15c482efb999d8f99b9", + downloaded_file_path = "aws-xml-protocols-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-jvm/1.0.16/http-jvm-1.0.16-sources.jar", + sha256 = "2c8e0881e79988310ad402a79ef9dcf604d68b675bdd9056343a5133e8edf191", + downloaded_file_path = "http-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-auth-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-jvm/1.0.16/http-auth-jvm-1.0.16-sources.jar", + sha256 = "78222f0442031fa2201d512885279b027385bd89e7760006ee27b7d825c2913c", + downloaded_file_path = "http-auth-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-auth-api-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-api-jvm/1.0.16/http-auth-api-jvm-1.0.16-sources.jar", + sha256 = "41f50535e53ddb3da31de70f65d80c8c3680055abc3de3336ff0ddbe9dab95ca", + downloaded_file_path = "http-auth-api-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-auth-aws-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-auth-aws-jvm/1.0.16/http-auth-aws-jvm-1.0.16-sources.jar", + sha256 = "318701ebe5a4cb2b9ef4602ee940e1011e3385629a3ab2b96dfbce8fa2b9319a", + downloaded_file_path = "http-auth-aws-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-client-engine-default-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-default-jvm/1.0.16/http-client-engine-default-jvm-1.0.16-sources.jar", + sha256 = "430597b6f8f43bdb1ae5987d564b156ea8a5e00658793db976e16c472f219320", + downloaded_file_path = "http-client-engine-default-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-http-client-engine-okhttp-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/http-client-engine-okhttp-jvm/1.0.16/http-client-engine-okhttp-jvm-1.0.16-sources.jar", + sha256 = "bbbcd3c14a7d4939f270b68547d90755b56c9b66f6d715964ab3bdcb0f17d252", + downloaded_file_path = "http-client-engine-okhttp-jvm-1.0.16-sources.jar" +) + +http_file( + name = "com_squareup_okhttp3-okhttp-jvm-5_0_0-alpha_11-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-jvm/5.0.0-alpha.11/okhttp-jvm-5.0.0-alpha.11-sources.jar", + sha256 = "93da6c712a017eb4625fd9093bb1305c670583cdaa2279cfe79f1e308b544e1e", + downloaded_file_path = "okhttp-jvm-5.0.0-alpha.11-sources.jar" +) + +http_file( + name = "com_squareup_okhttp3-okhttp-coroutines-jvm-5_0_0-alpha_11-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/squareup/okhttp3/okhttp-coroutines-jvm/5.0.0-alpha.11/okhttp-coroutines-jvm-5.0.0-alpha.11-sources.jar", + sha256 = "a97b8fb6bc30483991426ec3ec223c8d0f8994fa10980448c404077a13be9ec4", + downloaded_file_path = "okhttp-coroutines-jvm-5.0.0-alpha.11-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-identity-api-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/identity-api-jvm/1.0.16/identity-api-jvm-1.0.16-sources.jar", + sha256 = "a2cafc146661a3f9dd887cdd03cd5b39acd8babd5dfcf7772e226b35ecd103f5", + downloaded_file_path = "identity-api-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-jvm/1.0.16/serde-jvm-1.0.16-sources.jar", + sha256 = "303ce8427e5b0b4037f7cfa6b0e75470cb6c8c46166bb73c0dc628a988acee18", + downloaded_file_path = "serde-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-serde-xml-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/serde-xml-jvm/1.0.16/serde-xml-jvm-1.0.16-sources.jar", + sha256 = "fbd7cf3d16fe774af863d6ad9a460a2fef5c4c338b771f77e308aa50cba2df37", + downloaded_file_path = "serde-xml-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-telemetry-defaults-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/telemetry-defaults-jvm/1.0.16/telemetry-defaults-jvm-1.0.16-sources.jar", + sha256 = "900d1c6c0274a713fed0c03b02f278c32a4af049b6922f6d85db4cc82f5f2207", + downloaded_file_path = "telemetry-defaults-jvm-1.0.16-sources.jar" +) + +http_file( + name = "aws_smithy_kotlin-logging-slf4j2-jvm-1_0_16-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/aws/smithy/kotlin/logging-slf4j2-jvm/1.0.16/logging-slf4j2-jvm-1.0.16-sources.jar", + sha256 = "0bac498fe98ab2e4f25af1551a0df700e03ff628d0e909e41ceedc935591bb57", + downloaded_file_path = "logging-slf4j2-jvm-1.0.16-sources.jar" +) + +http_file( + name = "com_github_ajalt_clikt-clikt-jvm-5_0_3_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-jvm/5.0.3/clikt-jvm-5.0.3.jar", + sha256 = "9cd19e04782f461dffeb183d17e576984febb7934932cb406646ac41e428863b", + downloaded_file_path = "clikt-jvm-5.0.3.jar" +) + +http_file( + name = "com_github_ajalt_mordant-mordant-jvm-3_0_1_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-jvm/3.0.1/mordant-jvm-3.0.1.jar", + sha256 = "9ed3b976fcccc78da746d49866fa8ebb8f10530a93c544ea0420259a607dd95e", + downloaded_file_path = "mordant-jvm-3.0.1.jar" +) + +http_file( + name = "com_github_ajalt_mordant-mordant-core-jvm-3_0_1_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-core-jvm/3.0.1/mordant-core-jvm-3.0.1.jar", + sha256 = "9cf9b46d1f49f2d6cf2635462b29dc59e0c29b1fb2f085b3312888bbe9c7cd31", + downloaded_file_path = "mordant-core-jvm-3.0.1.jar" +) + +http_file( + name = "com_github_ajalt_colormath-colormath-jvm-3_6_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/colormath/colormath-jvm/3.6.0/colormath-jvm-3.6.0.jar", + sha256 = "59f741adfe62053066782d8b1a45afd06685a4bc64b33277e54876b993ed885c", + downloaded_file_path = "colormath-jvm-3.6.0.jar" +) + +http_file( + name = "com_github_ajalt_clikt-clikt-jvm-5_0_3-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-jvm/5.0.3/clikt-jvm-5.0.3-sources.jar", + sha256 = "42e19164ac1058a8f86f5929bad928d749e96376c5e58499924f441fff67f714", + downloaded_file_path = "clikt-jvm-5.0.3-sources.jar" +) + +http_file( + name = "com_github_ajalt_mordant-mordant-jvm-3_0_1-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-jvm/3.0.1/mordant-jvm-3.0.1-sources.jar", + sha256 = "3aa71b91497d7a6e10947949ee91039186c181fda3cc5418e49ccd5cbee98b62", + downloaded_file_path = "mordant-jvm-3.0.1-sources.jar" +) + +http_file( + name = "com_github_ajalt_mordant-mordant-core-jvm-3_0_1-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/mordant/mordant-core-jvm/3.0.1/mordant-core-jvm-3.0.1-sources.jar", + sha256 = "96c7a7138bce85cd7aa4d3359b0febd4f907ab4692e9434ac2a9cf364c2f7b09", + downloaded_file_path = "mordant-core-jvm-3.0.1-sources.jar" +) + +http_file( + name = "com_github_ajalt_colormath-colormath-jvm-3_6_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/colormath/colormath-jvm/3.6.0/colormath-jvm-3.6.0-sources.jar", + sha256 = "cd4baaaa928f21ec45b0cbf78f7071a94be9830bf0e3ed8f320e851f47b32864", + downloaded_file_path = "colormath-jvm-3.6.0-sources.jar" +) + +http_file( + name = "com_github_ajalt_clikt-clikt-core-jvm-5_0_3_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-core-jvm/5.0.3/clikt-core-jvm-5.0.3.jar", + sha256 = "69cba94a2291f74b78c88239cd8b67eaf3f08e0dad297749abe6477b3e9b81b1", + downloaded_file_path = "clikt-core-jvm-5.0.3.jar" +) + +http_file( + name = "com_github_ajalt_clikt-clikt-core-jvm-5_0_3-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/ajalt/clikt/clikt-core-jvm/5.0.3/clikt-core-jvm-5.0.3-sources.jar", + sha256 = "517d028e3b038ae9fa2cac9d536d1ddc78c35a463f3c65f524503c6f6699e4a6", + downloaded_file_path = "clikt-core-jvm-5.0.3-sources.jar" +) + +http_file( + name = "com_github_luben-zstd-jni-1_5_5-11_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/luben/zstd-jni/1.5.5-11/zstd-jni-1.5.5-11.jar", + sha256 = "d75b2ced6059f81ad23e021c554259b906b6c4f2991cb772409827569ead4c1a", + downloaded_file_path = "zstd-jni-1.5.5-11.jar" +) + +http_file( + name = "com_github_luben-zstd-jni-1_5_5-11-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/github/luben/zstd-jni/1.5.5-11/zstd-jni-1.5.5-11-sources.jar", + sha256 = "658dc0dc9832114d6e8665eb297ae432ad90ec1419fadbc97f974179137bb8c2", + downloaded_file_path = "zstd-jni-1.5.5-11-sources.jar" +) + +http_file( + name = "io_github_pdvrieze_xmlutil-core-jvm-0_86_3_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/core-jvm/0.86.3/core-jvm-0.86.3.jar", + sha256 = "91527d86fea04bd6183d140a09f10dab2dea727af12d27d9165ee342ea3d0ede", + downloaded_file_path = "core-jvm-0.86.3.jar" +) + +http_file( + name = "io_github_pdvrieze_xmlutil-core-jvm-0_86_3-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/core-jvm/0.86.3/core-jvm-0.86.3-sources.jar", + sha256 = "ef68ec64bfe82cd284b634edd19ec3630746644af53184ba70cbb4878002fdcb", + downloaded_file_path = "core-jvm-0.86.3-sources.jar" +) + +http_file( + name = "io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/serialization-jvm/0.86.3/serialization-jvm-0.86.3.jar", + sha256 = "9ce273dcb860b92a5bf2ec36ad1e2a11b41aed89c6c984ca73e87eff5eda1bd0", + downloaded_file_path = "serialization-jvm-0.86.3.jar" +) + +http_file( + name = "io_github_pdvrieze_xmlutil-serialization-jvm-0_86_3-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/github/pdvrieze/xmlutil/serialization-jvm/0.86.3/serialization-jvm-0.86.3-sources.jar", + sha256 = "6aa7410dead13532a0d254b5ce98d7e5db19f06743014ba76f084f86c2c0b561", + downloaded_file_path = "serialization-jvm-0.86.3-sources.jar" +) + http_file( name = "io_ktor-ktor-client-cio-jvm-3_1_3_http", url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-cio-jvm/3.1.3/ktor-client-cio-jvm-3.1.3.jar", @@ -1812,6 +2372,34 @@ http_file( downloaded_file_path = "ktor-client-core-jvm-3.1.3-sources.jar" ) +http_file( + name = "io_ktor-ktor-client-logging-jvm-3_1_3_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3.jar", + sha256 = "ecabf013b137f1a69c39cffeae1ba9e365659357b21688f1876ee6cf50fd00b9", + downloaded_file_path = "ktor-client-logging-jvm-3.1.3.jar" +) + +http_file( + name = "io_ktor-ktor-client-logging-jvm-3_1_3-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3-sources.jar", + sha256 = "4ac316b70a1fa7dd06c17926de41340d24c045b3a96e87139f5a7debb6d78a9a", + downloaded_file_path = "ktor-client-logging-jvm-3.1.3-sources.jar" +) + +http_file( + name = "io_ktor-ktor-client-mock-jvm-3_1_3_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3.jar", + sha256 = "2ae0d57bb9fceb8ee2aeda6536acc6f3e1de355984b4c9da720566b85f1525e8", + downloaded_file_path = "ktor-client-mock-jvm-3.1.3.jar" +) + +http_file( + name = "io_ktor-ktor-client-mock-jvm-3_1_3-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3-sources.jar", + sha256 = "8c47ed0e7a044a368ee139db4d9e372ab295931b8091fcc6027c6124de8eb7ef", + downloaded_file_path = "ktor-client-mock-jvm-3.1.3-sources.jar" +) + http_file( name = "io_ktor-ktor-events-jvm-3_1_3_http", url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-events-jvm/3.1.3/ktor-events-jvm-3.1.3.jar", @@ -2009,17 +2597,73 @@ http_file( ) http_file( - name = "org_fusesource_jansi-jansi-2_4_1_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1.jar", - sha256 = "2e5e775a9dc58ffa6bbd6aa6f099d62f8b62dcdeb4c3c3bbbe5cf2301bc2dcc1", - downloaded_file_path = "jansi-2.4.1.jar" + name = "com_jetbrains_intellij_idea-idea-community-build-zip-252_11244_http", + url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/com/jetbrains/intellij/idea/idea-community-build-zip/252.11244/idea-community-build-zip-252.11244.jar", + sha256 = "c86d2bfa23d5d23f916d3574591a166b3df259bea6c23d39abea597b34ba022f", + downloaded_file_path = "idea-community-build-zip-252.11244.jar" ) http_file( - name = "org_fusesource_jansi-jansi-2_4_1-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1-sources.jar", - sha256 = "f707511567a13ebf8c51164133770eb5a8e023e1d391bfbc6e7a0591c71729b8", - downloaded_file_path = "jansi-2.4.1-sources.jar" + name = "io_netty-netty-buffer-4_2_0_RC2_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2.jar", + sha256 = "74d953cec00048b892112f9602fc7b1113ea55337dc1ede32e449efcb2b231c7", + downloaded_file_path = "netty-buffer-4.2.0.RC2.jar" +) + +http_file( + name = "io_netty-netty-common-4_2_0_RC2_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2.jar", + sha256 = "cc65afe17daf2f9380d3c64460f5b10ec279f98b9461fbf0ccf62b3cb3ee6c37", + downloaded_file_path = "netty-common-4.2.0.RC2.jar" +) + +http_file( + name = "org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1_http", + url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/org/jetbrains/intellij/deps/fastutil/intellij-deps-fastutil/8.5.15-jb1/intellij-deps-fastutil-8.5.15-jb1.jar", + sha256 = "5a0d7557d494732a6165e4188eb11f10faca196350cad04d9950abd83e6dd148", + downloaded_file_path = "intellij-deps-fastutil-8.5.15-jb1.jar" +) + +http_file( + name = "com_dynatrace_hash4j-hash4j-0_20_0_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/dynatrace/hash4j/hash4j/0.20.0/hash4j-0.20.0.jar", + sha256 = "0181873ba85684d119134f5598432f5b63109a03adc455767a1500b2145df8a2", + downloaded_file_path = "hash4j-0.20.0.jar" +) + +http_file( + name = "com_jetbrains_intellij_idea-idea-community-build-zip-252_11244-sources_http", + url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/com/jetbrains/intellij/idea/idea-community-build-zip/252.11244/idea-community-build-zip-252.11244-sources.jar", + sha256 = "ca10b44853d641b0bfeb5ed57cca50d16892735821542bd7fd45e3b33314c566", + downloaded_file_path = "idea-community-build-zip-252.11244-sources.jar" +) + +http_file( + name = "io_netty-netty-buffer-4_2_0_RC2-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2-sources.jar", + sha256 = "e483fa26b8035c8c2a2306371a19e821b1c63a08465c31b9711e6db79e691715", + downloaded_file_path = "netty-buffer-4.2.0.RC2-sources.jar" +) + +http_file( + name = "io_netty-netty-common-4_2_0_RC2-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2-sources.jar", + sha256 = "5b3516f7d3f5d4884af89bd632713d779aba17b5a8eabc6366777201878d7e27", + downloaded_file_path = "netty-common-4.2.0.RC2-sources.jar" +) + +http_file( + name = "org_jetbrains_intellij_deps_fastutil-intellij-deps-fastutil-8_5_15-jb1-sources_http", + url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/org/jetbrains/intellij/deps/fastutil/intellij-deps-fastutil/8.5.15-jb1/intellij-deps-fastutil-8.5.15-jb1-sources.jar", + sha256 = "57d0e59cd6f6bd931d893ab0089bdcb13aee0ed78f1f8e85d88bc55b89815211", + downloaded_file_path = "intellij-deps-fastutil-8.5.15-jb1-sources.jar" +) + +http_file( + name = "com_dynatrace_hash4j-hash4j-0_20_0-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/dynatrace/hash4j/hash4j/0.20.0/hash4j-0.20.0-sources.jar", + sha256 = "63ddb785e1691849d571a78dfca9c311f2646d148fd7cbf1a60999e7e93a54bc", + downloaded_file_path = "hash4j-0.20.0-sources.jar" ) http_file( @@ -2036,6 +2680,34 @@ http_file( downloaded_file_path = "util-zip-squashed-252.16432-sources.jar" ) +http_file( + name = "org_fusesource_jansi-jansi-2_4_1_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1.jar", + sha256 = "2e5e775a9dc58ffa6bbd6aa6f099d62f8b62dcdeb4c3c3bbbe5cf2301bc2dcc1", + downloaded_file_path = "jansi-2.4.1.jar" +) + +http_file( + name = "org_fusesource_jansi-jansi-2_4_1-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/fusesource/jansi/jansi/2.4.1/jansi-2.4.1-sources.jar", + sha256 = "f707511567a13ebf8c51164133770eb5a8e023e1d391bfbc6e7a0591c71729b8", + downloaded_file_path = "jansi-2.4.1-sources.jar" +) + +http_file( + name = "org_tukaani-xz-1_9_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/tukaani/xz/1.9/xz-1.9.jar", + sha256 = "211b306cfc44f8f96df3a0a3ddaf75ba8c5289eed77d60d72f889bb855f535e5", + downloaded_file_path = "xz-1.9.jar" +) + +http_file( + name = "org_tukaani-xz-1_9-sources_http", + url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/org/tukaani/xz/1.9/xz-1.9-sources.jar", + sha256 = "5befa47f06b90e752f035191dde7f2deb59f36000f1ca6cc77d2362a82b6f462", + downloaded_file_path = "xz-1.9-sources.jar" +) + http_file( name = "org_jetbrains_intellij_deps_ift-git-learning-project-212_0_2_http", url = "https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-dependencies/org/jetbrains/intellij/deps/ift/git-learning-project/212.0.2/git-learning-project-212.0.2.jar", @@ -6509,20 +7181,6 @@ http_file( downloaded_file_path = "ktor-client-java-jvm-3.1.3-sources.jar" ) -http_file( - name = "io_ktor-ktor-client-mock-jvm-3_1_3_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3.jar", - sha256 = "2ae0d57bb9fceb8ee2aeda6536acc6f3e1de355984b4c9da720566b85f1525e8", - downloaded_file_path = "ktor-client-mock-jvm-3.1.3.jar" -) - -http_file( - name = "io_ktor-ktor-client-mock-jvm-3_1_3-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-mock-jvm/3.1.3/ktor-client-mock-jvm-3.1.3-sources.jar", - sha256 = "8c47ed0e7a044a368ee139db4d9e372ab295931b8091fcc6027c6124de8eb7ef", - downloaded_file_path = "ktor-client-mock-jvm-3.1.3-sources.jar" -) - http_file( name = "io_ktor-ktor-client-okhttp-jvm-3_1_3_http", url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-okhttp-jvm/3.1.3/ktor-client-okhttp-jvm-3.1.3.jar", @@ -8070,20 +8728,6 @@ http_file( downloaded_file_path = "ktor-client-encoding-jvm-3.1.3-sources.jar" ) -http_file( - name = "io_ktor-ktor-client-logging-jvm-3_1_3_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3.jar", - sha256 = "ecabf013b137f1a69c39cffeae1ba9e365659357b21688f1876ee6cf50fd00b9", - downloaded_file_path = "ktor-client-logging-jvm-3.1.3.jar" -) - -http_file( - name = "io_ktor-ktor-client-logging-jvm-3_1_3-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/ktor/ktor-client-logging-jvm/3.1.3/ktor-client-logging-jvm-3.1.3-sources.jar", - sha256 = "4ac316b70a1fa7dd06c17926de41340d24c045b3a96e87139f5a7debb6d78a9a", - downloaded_file_path = "ktor-client-logging-jvm-3.1.3-sources.jar" -) - http_file( name = "com_miglayout-miglayout-swing-11_4_2_http", url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/com/miglayout/miglayout-swing/11.4.2/miglayout-swing-11.4.2.jar", @@ -11647,34 +12291,6 @@ http_file( downloaded_file_path = "mockito-kotlin-6.0.0-sources.jar" ) -http_file( - name = "io_netty-netty-buffer-4_2_0_RC2_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2.jar", - sha256 = "74d953cec00048b892112f9602fc7b1113ea55337dc1ede32e449efcb2b231c7", - downloaded_file_path = "netty-buffer-4.2.0.RC2.jar" -) - -http_file( - name = "io_netty-netty-common-4_2_0_RC2_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2.jar", - sha256 = "cc65afe17daf2f9380d3c64460f5b10ec279f98b9461fbf0ccf62b3cb3ee6c37", - downloaded_file_path = "netty-common-4.2.0.RC2.jar" -) - -http_file( - name = "io_netty-netty-buffer-4_2_0_RC2-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-buffer/4.2.0.RC2/netty-buffer-4.2.0.RC2-sources.jar", - sha256 = "e483fa26b8035c8c2a2306371a19e821b1c63a08465c31b9711e6db79e691715", - downloaded_file_path = "netty-buffer-4.2.0.RC2-sources.jar" -) - -http_file( - name = "io_netty-netty-common-4_2_0_RC2-sources_http", - url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-common/4.2.0.RC2/netty-common-4.2.0.RC2-sources.jar", - sha256 = "5b3516f7d3f5d4884af89bd632713d779aba17b5a8eabc6366777201878d7e27", - downloaded_file_path = "netty-common-4.2.0.RC2-sources.jar" -) - http_file( name = "io_netty-netty-codec-compression-4_2_0_RC2_http", url = "https://cache-redirector.jetbrains.com/repo1.maven.org/maven2/io/netty/netty-codec-compression/4.2.0.RC2/netty-codec-compression-4.2.0.RC2.jar",