diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 156595c94..80559d3d6 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -48,157 +48,20 @@ tasks: # cannot be used with remote builds) - "--strategy=KotlinCompile=remote" - "--config=rbe" -# TODO: Enable these tests once this example is building against the release rules_kotlin binary -# example-android-5.x: -# name: "Example - Android using Bazel 5.x" -# platform: ubuntu1804 -# working_directory: examples/android -# bazel: 5.4.1 -# test_targets: -# - //app:all - example-android-6.x: - name: "Example - Android using Bazel 6.x" - platform: ubuntu1804 - working_directory: examples/android - bazel: 6.4.0 - test_flags: - - "--enable_bzlmod=false" - test_targets: - - //app:all - example-android: - name: "Example - Android" - platform: ubuntu1804 - working_directory: examples/android - bazel: 8.0.0-pre.20240206.3 - test_flags: - - "--incompatible_enable_android_toolchain_resolution" - - "--android_platforms=//:arm64-v8a" - - "--enable_bzlmod=false" - test_targets: - - //app:all - example-bzlmod-android: - name: "Example Bzlmod - Android" - platform: ubuntu1804 - working_directory: examples/android - bazel: 8.0.0-pre.20240206.3 - test_flags: - - "--incompatible_enable_android_toolchain_resolution" - - "--android_platforms=//:arm64-v8a" - - "--enable_bzlmod=true" - test_targets: - - //app:all - example-ksp: - name: "Example - Android KSP" - platform: ubuntu1804 - working_directory: examples/ksp - test_targets: - - //... - example-associates: - name: "Example - Associates" - platform: ubuntu1804 - working_directory: examples/associates - test_targets: - - //... - example-anvil: - name: "Example - Anvil" - platform: ubuntu1804 - working_directory: examples/anvil - bazel: 8.0.0-pre.20240206.3 - test_flags: - - "--incompatible_enable_android_toolchain_resolution" - - "--android_platforms=//:arm64-v8a" - test_targets: - - //app:all - example-plugins: - name: "Example - Plugins" - platform: ubuntu1804 - working_directory: examples/plugin - test_targets: - - //... - example-multiplex: - name: "Example - Multiplex" - platform: ubuntu1804 - working_directory: examples/multiplex - build_flags: - - "--enable_bzlmod=false" - build_targets: - - //... - example-bzlmod-multiplex: - name: "Example Bzlmod - Multiplex" - platform: ubuntu1804 - working_directory: examples/multiplex - build_flags: - - "--enable_bzlmod=true" - build_targets: - - //... - examples-trivial-ubuntu1804: - name: "Example - Trivial (Ubuntu 18.04)" - platform: ubuntu1804 - working_directory: examples/trivial - include_json_profile: - - build - - test - build_targets: - - //... - test_targets: - - //... - examples-trivial-bzlmod-ubuntu1804: - name: "Example - Trivial Bzlmod (Ubuntu 18.04)" - platform: ubuntu1804 - working_directory: examples/trivial - build_flags: - - "--enable_bzlmod=true" - test_flags: - - "--enable_bzlmod=true" - include_json_profile: - - build - - test - build_targets: - - //... - test_targets: - - //... - examples-trivial-windows: - name: "Example - Trivial (Windows)" - platform: windows - working_directory: examples/trivial - include_json_profile: - - build - - test - build_targets: - - //... - test_flags: - - "--enable_runfiles" - test_targets: - - //... - examples-dagger: - name: "Example - Dagger" - platform: ubuntu1804 - working_directory: examples/dagger - include_json_profile: - - build - - test - build_targets: - - //... -# examples-nodejs: -# name: Example - Node -# platform: ubuntu1804 -# working_directory: examples/node -# include_json_profile: -# - build -# - test -# build_targets: -# - //coroutines-helloworld/... -# - //express/... - example-jetpack-compose: - name: "Example - Jetpack Compose" - platform: ubuntu1804 - working_directory: examples/jetpack_compose - bazel: 8.0.0-pre.20240206.3 - test_flags: - - "--incompatible_enable_android_toolchain_resolution" - - "--android_platforms=//:arm64-v8a" - test_targets: - - //app:all + bazel_integration_tests_ubuntu1804: + name: "Bazel Integration Tests (Ubuntu 18.04)" + platform: ubuntu1804 + test_flags: + - "--enable_bzlmod=true" + test_targets: + - //examples:all + bazel_integration_tests_ubuntu2004: + name: "Bazel Integration Tests (Ubuntu 20.04)" + platform: ubuntu2004 + test_flags: + - "--enable_bzlmod=true" + test_targets: + - //examples:all stardoc: name: Stardoc api documentation platform: ubuntu1804 diff --git a/.bazelignore b/.bazelignore index 471232e09..f1cffcd7d 100644 --- a/.bazelignore +++ b/.bazelignore @@ -1,2 +1,2 @@ # Exclude examples from //...:all -examples +examples/* diff --git a/.bazelproject b/.bazelproject index 7e950212c..16d40008d 100644 --- a/.bazelproject +++ b/.bazelproject @@ -18,15 +18,7 @@ directories: . targets: - //:all_local_tests - # These targets are built for the ide only. Primary purpose is to ensure the builder can build the targets, but it's - # also a good way of testing the intellij plugin. - //src/main/kotlin/io/bazel/kotlin/builder/tasks:tasks_for_ide - //src/main/kotlin/io/bazel/kotlin/builder/utils:utils_for_ide - //src/main/kotlin/io/bazel/kotlin/builder/toolchain:toolchain_for_ide - //src/main/kotlin/io/bazel/kotlin/compiler:compiler_for_ide - //kotlin:stardoc - //src/main/starlark/core/repositories:all + //src/... test_sources: src/test/* @@ -37,4 +29,4 @@ additional_languages: import_run_configurations: src/test/Bazel_all_local_tests.xml -android_sdk_platform: android-31 \ No newline at end of file +#android_sdk_platform: android-31 \ No newline at end of file diff --git a/.bazelrc b/.bazelrc index 382ca66b1..42862460f 100644 --- a/.bazelrc +++ b/.bazelrc @@ -7,5 +7,7 @@ build --strategy=KotlinCompile=worker build --test_output=all build --verbose_failures +try-import %workspace%/bit-ignore.bazelrc + # User-specific .bazelrc try-import %workspace%/user.bazelrc diff --git a/BUILD b/BUILD index 9dd34bf8a..bd8e153d3 100644 --- a/BUILD +++ b/BUILD @@ -75,3 +75,13 @@ release_archive( "//third_party:pkg", ], ) + +# This target collects all of the parent workspace files needed by the child workspaces. +filegroup( + name = "release_repositories", + # Include every package that is required by the child workspaces. + srcs = [ + ":rules_kotlin_release", + ], + visibility = ["//:__subpackages__"], +) diff --git a/MODULE.bazel b/MODULE.bazel index a7cf644a8..7036d1262 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -5,11 +5,11 @@ module( repo_name = "rules_kotlin", ) -bazel_dep(name = "platforms", version = "0.0.6") +bazel_dep(name = "platforms", version = "0.0.7") bazel_dep(name = "bazel_skylib", version = "1.6.1") -bazel_dep(name = "rules_java", version = "7.2.0") +bazel_dep(name = "rules_java", version = "7.4.0") bazel_dep(name = "rules_python", version = "0.23.1") -bazel_dep(name = "rules_cc", version = "0.0.8") +bazel_dep(name = "rules_cc", version = "0.0.9") rules_kotlin_extensions = use_extension("//src/main/starlark/core/repositories:bzlmod_setup.bzl", "rules_kotlin_extensions") use_repo( @@ -17,6 +17,7 @@ use_repo( "buildkite_config", "com_github_google_ksp", "com_github_jetbrains_kotlin", + "com_github_jetbrains_kotlin_git", "com_github_pinterest_ktlint", "released_rules_kotlin", "rules_android", @@ -59,8 +60,8 @@ maven.install( "com.google.protobuf:protobuf-java-util:3.6.0", "com.google.guava:guava:27.1-jre", "com.google.truth:truth:0.45", - "com.google.auto.service:auto-service:1.0.1", - "com.google.auto.service:auto-service-annotations:1.0.1", + "com.google.auto.service:auto-service:1.1.1", + "com.google.auto.service:auto-service-annotations:1.1.1", "com.google.auto.value:auto-value:1.10.1", "com.google.auto.value:auto-value-annotations:1.10.1", "com.google.dagger:dagger:2.51", @@ -68,6 +69,7 @@ maven.install( "com.google.dagger:dagger-producers:2.51", "javax.annotation:javax.annotation-api:1.3.2", "javax.inject:javax.inject:1", + "org.apache.commons:commons-compress:1.26.2", "org.pantsbuild:jarjar:1.7.2", "org.jetbrains.kotlinx:atomicfu-js:0.15.2", "org.jetbrains.kotlinx:kotlinx-serialization-runtime:1.0-M1-1.4.0-rc", @@ -75,7 +77,6 @@ maven.install( "com.squareup.moshi:moshi:1.15.0", "com.squareup.moshi:moshi-kotlin:1.15.0", "com.squareup.moshi:moshi-kotlin-codegen:1.15.0", - "com.google.auto.service:auto-service-annotations:jar:1.1.1", ], fail_if_repin_required = True, fetch_sources = True, @@ -93,3 +94,10 @@ bazel_dep(name = "stardoc", version = "0.5.6", repo_name = "io_bazel_stardoc") bazel_dep(name = "rules_proto", version = "5.3.0-21.7") bazel_dep(name = "rules_testing", version = "0.5.0", dev_dependency = True) +bazel_dep(name = "rules_bazel_integration_test", version = "0.23.0", dev_dependency = True) + +bazel_binaries = use_extension("@rules_bazel_integration_test//:extensions.bzl","bazel_binaries",dev_dependency = True) +bazel_binaries.download(version_file = "//:.bazelversion") +bazel_binaries.download(version = "6.0.0") +bazel_binaries.download(version = "last_green") +use_repo(bazel_binaries, "bazel_binaries", "bazel_binaries_bazelisk", "build_bazel_bazel_.bazelversion", "build_bazel_bazel_6_0_0", "build_bazel_bazel_last_green") diff --git a/examples/BUILD b/examples/BUILD index dea9df6e3..830b7b0a4 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -1,14 +1,59 @@ -# Copyright 2018 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -package(default_visibility = ["//visibility:private"]) +load("//src/main/starlark/release:packager.bzl", "release_archive") +load("@bazel_binaries//:defs.bzl", "bazel_binaries") +load( + "@rules_bazel_integration_test//bazel_integration_test:defs.bzl", + "bazel_integration_test", + "bazel_integration_tests", + "default_test_runner", + "integration_test_utils", +) + +genrule( + name = "update_bit_ignore", + srcs = [], + outs = [ + "update_bit_ignore.sh", + ], + cmd = "\n".join([ + "cat <<'EOS' > $@", + "#!/usr/bin/env bash", + "cd $${BUILD_WORKSPACE_DIRECTORY}", + "echo 'common --deleted_packages='$$(find examples -type 'd' | grep -v '^examples$$' | tr '\n' ',' | sed 's/,$$//')" + + "> bit-ignore.bazelrc", + "EOS", + ]), + executable = True, +) + +[ + bazel_integration_tests( + name = "%s_test" % example, + bazel_versions = bazel_binaries.versions.all, + test_runner = "//src/main/kotlin/io/bazel/kotlin/test:BazelIntegrationTestRunner", + workspace_files = glob(["%s/**/*" % example]), + workspace_path = example, + ) + for example in { + file.partition("/")[0]: True + for file in glob( + ["**/*"], + exclude = ["*"], + ) + } +] + +# By default, the integration test targets are tagged as `manual`. This +# prevents the targets from being found from most target expansion (e.g. +# `//...`, `:all`). To easily execute a group of integration tests, you may +# want to define a test suite which includes the desired test targets. +#test_suite( +# name = "all_integration_tests", +# # If you don't apply the test tags to the test suite, the test suite will +# # be found when `bazel test //...` is executed. +# tags = integration_test_utils.DEFAULT_INTEGRATION_TEST_TAGS, +# tests = integration_test_utils.bazel_integration_test_names( +# "simple_test", +# bazel_binaries.versions.other, +# ), +# visibility = ["//:__subpackages__"], +#) diff --git a/examples/README.md b/examples/README.md index dac8e5080..c95a7351b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1 +1,8 @@ -The examples directory is for test applications. These could be used in tests or for verifying the intellij plugin. \ No newline at end of file +# Examples + +These are series of code samples useful for configuring a variety of use cases. They also serve as integration tests. + +## Adding a new integration test +1. Create a new workspace in the `examples` directory +2. Ensure that the `rules_kotlin` repository is named `rules_kotlin` +3. Update the bazel excluded packages via `bazel run //examples:update_bit_ignore` \ No newline at end of file diff --git a/examples/android/MODULE.bazel b/examples/android/MODULE.bazel index 6eb68a3fa..c3b0baa0f 100644 --- a/examples/android/MODULE.bazel +++ b/examples/android/MODULE.bazel @@ -8,11 +8,7 @@ bazel_dep(name = "bazel_skylib", version = "1.2.1") bazel_dep(name = "rules_robolectric", version = "4.10.3", repo_name = "robolectric") bazel_dep(name = "rules_java", version = "6.4.0") bazel_dep(name = "platforms", version = "0.0.7") -bazel_dep(name = "rules_kotlin") -local_path_override( - module_name = "rules_kotlin", - path = "../..", -) +bazel_dep(name = "rules_kotlin", version = "1.9.5") bazel_dep(name = "rules_jvm_external", version = "5.3") diff --git a/examples/android/WORKSPACE b/examples/android/WORKSPACE index 2f57f6148..79d80930e 100644 --- a/examples/android/WORKSPACE +++ b/examples/android/WORKSPACE @@ -1,19 +1,13 @@ workspace(name = "android_example") -local_repository( - name = "release_archive", - path = "../../src/main/starlark/release_archive", -) - -load("@release_archive//:repository.bzl", "archive_repository") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -archive_repository( +http_archive( name = "rules_kotlin", - local_path = "../..", + sha256 = "34e8c0351764b71d78f76c8746e98063979ce08dcf1a91666f3f3bc2949a533d", + url = "https://github.com/bazelbuild/rules_kotlin/releases/download/v1.9.5/rules_kotlin-v1.9.5.tar.gz", ) -load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories", "versions") - kotlin_repositories() register_toolchains("//bzl:experimental_toolchain") diff --git a/kotlin_rules_maven_install.json b/kotlin_rules_maven_install.json index 522919adb..7742ee2e5 100755 --- a/kotlin_rules_maven_install.json +++ b/kotlin_rules_maven_install.json @@ -1,18 +1,17 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1193919207, - "__RESOLVED_ARTIFACTS_HASH": 368834518, + "__INPUT_ARTIFACTS_HASH": -194774941, + "__RESOLVED_ARTIFACTS_HASH": 860729270, "conflict_resolution": { - "com.google.auto.service:auto-service-annotations:1.0.1": "com.google.auto.service:auto-service-annotations:1.1.1", "com.google.guava:guava:27.1-jre": "com.google.guava:guava:33.0.0-jre" }, "artifacts": { "com.google.auto.service:auto-service": { "shasums": { - "jar": "88d469a5392dc2a292dfa20432591a584c29fae417cfd88b72620f35ec795acf", - "sources": "704a261cdd500ac47636d09b92a3a2b75f3b9484a3ce8fc3b08f66574fc1fbb2" + "jar": "1f48f451503e623daba7d9ed368cca0f81e1e3815653a4560113e12c0129ebd5", + "sources": "6965a4761538f4073befe2967e11e7f6f2ce674a5c527de3c91e3ba88ec07432" }, - "version": "1.0.1" + "version": "1.1.1" }, "com.google.auto.service:auto-service-annotations": { "shasums": { @@ -37,10 +36,10 @@ }, "com.google.auto:auto-common": { "shasums": { - "jar": "326d674b411ea67505273f9ade5311c15bca50644b5211a6c309c9aee590a20a", - "sources": "5ed509e12f9bf2c90dc5c26e4e94eb97a7e747c59ad527b018b9567b398b56b1" + "jar": "f43f29fe2a6ebaf04b2598cdeec32a4e346d49a9404e990f5fc19c19f3a28d0e", + "sources": "6802fc6e48f84cacadab9418bc8eba732f4c6a4189fc8569b1f619cb88112b25" }, - "version": "1.2" + "version": "1.2.1" }, "com.google.code.findbugs:jsr305": { "shasums": { @@ -217,6 +216,20 @@ }, "version": "1.13.2" }, + "commons-codec:commons-codec": { + "shasums": { + "jar": "f700de80ac270d0344fdea7468201d8b9c805e5c648331c3619f2ee067ccfc59", + "sources": "9869277d653510f670039b77945befe71c1ca34ef2e281855bd86e6605aab4a6" + }, + "version": "1.17.0" + }, + "commons-io:commons-io": { + "shasums": { + "jar": "f41f7baacd716896447ace9758621f62c1c6b0a91d89acee488da26fc477c84f", + "sources": "fcfe84e39fb44e38a0ea0ab0815b53adea6fff89c7b72535bc42495f400cb9a1" + }, + "version": "2.16.1" + }, "dev.zacsweers.autoservice:auto-service-ksp": { "shasums": { "jar": "290457e26274b41f625f96b24e033dd18639c6353a6f81304f0a15e2b22eec35", @@ -280,12 +293,19 @@ }, "version": "1.9.9" }, + "org.apache.commons:commons-compress": { + "shasums": { + "jar": "9168a03141d8fc7eda21a2360d83cc0412bcbb1d6204d992bd48c2573cb3c6b8", + "sources": "89f2ac13872e1ac0024dc77573a0da8e8e0da2dda58973f19244f6e60f08d138" + }, + "version": "1.26.2" + }, "org.apache.commons:commons-lang3": { "shasums": { - "jar": "734c8356420cc8e30c795d64fd1fcd5d44ea9d90342a2cc3262c5158fbc6d98b", - "sources": "4709f16a9e0f8fd83ae155083d63044e23045aac8f6f0183a2db09f492491b12" + "jar": "7b96bf3ee68949abb5bc465559ac270e0551596fa34523fddf890ec418dde13c", + "sources": "ab3b86afb898f1026dbe43aaf71e9c1d719ec52d6e41887b362d86777c299b6f" }, - "version": "3.4" + "version": "3.14.0" }, "org.apache.maven:maven-artifact": { "shasums": { @@ -581,6 +601,11 @@ "org.apache.ant:ant": [ "org.apache.ant:ant-launcher" ], + "org.apache.commons:commons-compress": [ + "commons-codec:commons-codec", + "commons-io:commons-io", + "org.apache.commons:commons-lang3" + ], "org.apache.maven:maven-artifact": [ "org.apache.commons:commons-lang3", "org.codehaus.plexus:plexus-utils" @@ -959,6 +984,32 @@ "com.squareup:kotlinpoet-ksp": [ "com.squareup.kotlinpoet.ksp" ], + "commons-codec:commons-codec": [ + "org.apache.commons.codec", + "org.apache.commons.codec.binary", + "org.apache.commons.codec.cli", + "org.apache.commons.codec.digest", + "org.apache.commons.codec.language", + "org.apache.commons.codec.language.bm", + "org.apache.commons.codec.net" + ], + "commons-io:commons-io": [ + "org.apache.commons.io", + "org.apache.commons.io.build", + "org.apache.commons.io.channels", + "org.apache.commons.io.charset", + "org.apache.commons.io.comparator", + "org.apache.commons.io.file", + "org.apache.commons.io.file.attribute", + "org.apache.commons.io.file.spi", + "org.apache.commons.io.filefilter", + "org.apache.commons.io.function", + "org.apache.commons.io.input", + "org.apache.commons.io.input.buffer", + "org.apache.commons.io.monitor", + "org.apache.commons.io.output", + "org.apache.commons.io.serialization" + ], "dev.zacsweers.autoservice:auto-service-ksp": [ "dev.zacsweers.autoservice.ksp" ], @@ -1086,19 +1137,63 @@ "org.apache.ant:ant-launcher": [ "org.apache.tools.ant.launch" ], + "org.apache.commons:commons-compress": [ + "org.apache.commons.compress", + "org.apache.commons.compress.archivers", + "org.apache.commons.compress.archivers.ar", + "org.apache.commons.compress.archivers.arj", + "org.apache.commons.compress.archivers.cpio", + "org.apache.commons.compress.archivers.dump", + "org.apache.commons.compress.archivers.examples", + "org.apache.commons.compress.archivers.jar", + "org.apache.commons.compress.archivers.sevenz", + "org.apache.commons.compress.archivers.tar", + "org.apache.commons.compress.archivers.zip", + "org.apache.commons.compress.changes", + "org.apache.commons.compress.compressors", + "org.apache.commons.compress.compressors.brotli", + "org.apache.commons.compress.compressors.bzip2", + "org.apache.commons.compress.compressors.deflate", + "org.apache.commons.compress.compressors.deflate64", + "org.apache.commons.compress.compressors.gzip", + "org.apache.commons.compress.compressors.lz4", + "org.apache.commons.compress.compressors.lz77support", + "org.apache.commons.compress.compressors.lzma", + "org.apache.commons.compress.compressors.lzw", + "org.apache.commons.compress.compressors.pack200", + "org.apache.commons.compress.compressors.snappy", + "org.apache.commons.compress.compressors.xz", + "org.apache.commons.compress.compressors.z", + "org.apache.commons.compress.compressors.zstandard", + "org.apache.commons.compress.harmony", + "org.apache.commons.compress.harmony.archive.internal.nls", + "org.apache.commons.compress.harmony.pack200", + "org.apache.commons.compress.harmony.unpack200", + "org.apache.commons.compress.harmony.unpack200.bytecode", + "org.apache.commons.compress.harmony.unpack200.bytecode.forms", + "org.apache.commons.compress.java.util.jar", + "org.apache.commons.compress.parallel", + "org.apache.commons.compress.utils" + ], "org.apache.commons:commons-lang3": [ "org.apache.commons.lang3", + "org.apache.commons.lang3.arch", "org.apache.commons.lang3.builder", + "org.apache.commons.lang3.compare", "org.apache.commons.lang3.concurrent", + "org.apache.commons.lang3.concurrent.locks", "org.apache.commons.lang3.event", "org.apache.commons.lang3.exception", + "org.apache.commons.lang3.function", "org.apache.commons.lang3.math", "org.apache.commons.lang3.mutable", "org.apache.commons.lang3.reflect", + "org.apache.commons.lang3.stream", "org.apache.commons.lang3.text", "org.apache.commons.lang3.text.translate", "org.apache.commons.lang3.time", - "org.apache.commons.lang3.tuple" + "org.apache.commons.lang3.tuple", + "org.apache.commons.lang3.util" ], "org.apache.maven:maven-artifact": [ "org.apache.maven.artifact", @@ -1423,6 +1518,10 @@ "com.squareup:kotlinpoet-ksp", "com.squareup:kotlinpoet-ksp:jar:sources", "com.squareup:kotlinpoet:jar:sources", + "commons-codec:commons-codec", + "commons-codec:commons-codec:jar:sources", + "commons-io:commons-io", + "commons-io:commons-io:jar:sources", "dev.zacsweers.autoservice:auto-service-ksp", "dev.zacsweers.autoservice:auto-service-ksp:jar:sources", "javax.annotation:javax.annotation-api", @@ -1441,6 +1540,8 @@ "org.apache.ant:ant-launcher", "org.apache.ant:ant-launcher:jar:sources", "org.apache.ant:ant:jar:sources", + "org.apache.commons:commons-compress", + "org.apache.commons:commons-compress:jar:sources", "org.apache.commons:commons-lang3", "org.apache.commons:commons-lang3:jar:sources", "org.apache.maven:maven-artifact", @@ -1554,6 +1655,10 @@ "com.squareup:kotlinpoet-ksp", "com.squareup:kotlinpoet-ksp:jar:sources", "com.squareup:kotlinpoet:jar:sources", + "commons-codec:commons-codec", + "commons-codec:commons-codec:jar:sources", + "commons-io:commons-io", + "commons-io:commons-io:jar:sources", "dev.zacsweers.autoservice:auto-service-ksp", "dev.zacsweers.autoservice:auto-service-ksp:jar:sources", "javax.annotation:javax.annotation-api", @@ -1572,6 +1677,8 @@ "org.apache.ant:ant-launcher", "org.apache.ant:ant-launcher:jar:sources", "org.apache.ant:ant:jar:sources", + "org.apache.commons:commons-compress", + "org.apache.commons:commons-compress:jar:sources", "org.apache.commons:commons-lang3", "org.apache.commons:commons-lang3:jar:sources", "org.apache.maven:maven-artifact", @@ -1685,6 +1792,10 @@ "com.squareup:kotlinpoet-ksp", "com.squareup:kotlinpoet-ksp:jar:sources", "com.squareup:kotlinpoet:jar:sources", + "commons-codec:commons-codec", + "commons-codec:commons-codec:jar:sources", + "commons-io:commons-io", + "commons-io:commons-io:jar:sources", "dev.zacsweers.autoservice:auto-service-ksp", "dev.zacsweers.autoservice:auto-service-ksp:jar:sources", "javax.annotation:javax.annotation-api", @@ -1703,6 +1814,8 @@ "org.apache.ant:ant-launcher", "org.apache.ant:ant-launcher:jar:sources", "org.apache.ant:ant:jar:sources", + "org.apache.commons:commons-compress", + "org.apache.commons:commons-compress:jar:sources", "org.apache.commons:commons-lang3", "org.apache.commons:commons-lang3:jar:sources", "org.apache.maven:maven-artifact", diff --git a/src/main/kotlin/io/bazel/kotlin/builder/utils/BazelRunFiles.kt b/src/main/kotlin/io/bazel/kotlin/builder/utils/BazelRunFiles.kt index 3003763de..1a68deef2 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/utils/BazelRunFiles.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/utils/BazelRunFiles.kt @@ -18,6 +18,8 @@ package io.bazel.kotlin.builder.utils import com.google.devtools.build.runfiles.Runfiles import java.io.File import java.io.FileNotFoundException +import java.nio.file.FileSystem +import kotlin.io.path.exists /** Utility class for getting runfiles on windows and *nix. */ object BazelRunFiles { @@ -26,6 +28,22 @@ object BazelRunFiles { Runfiles.preload().unmapped() } + @JvmStatic + fun resolveVerifiedFromProperty(fileSystem: FileSystem, key: String) = + System.getProperty(key) + ?.let { path -> runfiles.rlocation(path) } + ?.let { fileSystem.getPath(it) } + ?.also { + if (!it.exists()) { + throw IllegalStateException( + "$it does not exist in the runfiles!", + ) + } + } + ?: let { + throw FileNotFoundException("no reference for $key in ${System.getProperties()}") + } + /** * Resolve a run file on windows or *nix. */ @@ -45,3 +63,5 @@ object BazelRunFiles { throw FileNotFoundException("no reference for $key in ${System.getProperties()}") } } + + diff --git a/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel new file mode 100644 index 000000000..53656c1a1 --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/test/BUILD.bazel @@ -0,0 +1,22 @@ +load("//kotlin:jvm.bzl", "kt_jvm_binary") + +kt_jvm_binary( + name = "BazelIntegrationTestRunner", + srcs = [ + "BazelIntegrationTestRunner.kt", + ], + data = [ + "//:rules_kotlin_release", + ], + jvm_flags = [ + "-D@rules_kotlin...rules_kotlin_release=$(rlocationpath //:rules_kotlin_release)", + ], + main_class = "io.bazel.kotlin.test.BazelIntegrationTestRunner", + visibility = [ + "//visibility:public", + ], + deps = [ + "//src/main/kotlin/io/bazel/kotlin/builder/utils", + "@kotlin_rules_maven//:org_apache_commons_commons_compress", + ], +) diff --git a/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt b/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt new file mode 100644 index 000000000..32012fa0c --- /dev/null +++ b/src/main/kotlin/io/bazel/kotlin/test/BazelIntegrationTestRunner.kt @@ -0,0 +1,113 @@ +package io.bazel.kotlin.test + + +import io.bazel.kotlin.builder.utils.BazelRunFiles +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream +import java.nio.charset.StandardCharsets.UTF_8 +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.inputStream + +object BazelIntegrationTestRunner { + @JvmStatic + fun main(args: Array) { + val fs = FileSystems.getDefault() + val bazel = fs.getPath(System.getenv("BIT_BAZEL_BINARY")) + val workspace = fs.getPath(System.getenv("BIT_WORKSPACE_DIR")) + val unpack = workspace.resolve("third_party").resolve("rules_kotlin").createDirectories() + TarArchiveInputStream( + BazelRunFiles.resolveVerifiedFromProperty( + fs, + "@rules_kotlin...rules_kotlin_release", + ).inputStream(), + ).use { stream -> + generateSequence(stream::getNextEntry).map { entry -> + val destination = unpack.resolve(entry.path) + when { + entry.isDirectory -> destination.createDirectories() + entry.isFile -> Files.write( + destination.apply { parent.createDirectories() }, + stream.readBytes(), + ) + + else -> throw NotImplementedError(entry.toString()) + } + } + } + + listOf(true, false) + .filter { bzlmod -> + bzlmod && workspace.hasModule() || !bzlmod && workspace.hasWorkspace() + } + .forEach { bzlmod -> + bazel.run( + workspace, + "info", + "--enable_bzlmod=$bzlmod", + "--override_repository=rules_kotlin=third_party/rules_kotlin", + ).onFailure { err -> + throw err + } + bazel.run( + workspace, + "build", + "--enable_bzlmod=$bzlmod", + "--override_repository=rules_kotlin=third_party/rules_kotlin", + "//...", + ).onFailure { err -> + throw err + } + bazel.run( + workspace, + "query", + "--enable_bzlmod=$bzlmod", + "--override_repository=rules_kotlin=third_party/rules_kotlin", + "kind(\".*_test\", \"//...\")", + ).onFailure { + err -> + throw err + }.onSuccess { process -> + if (process.inputStream.readAllBytes().isNotEmpty()) { + bazel.run( + workspace, + "test", + "--enable_bzlmod=$bzlmod", + "--override_repository=rules_kotlin=third_party/rules_kotlin", + "//...", + ).onFailure { err -> + throw err + } + } + } + + } + } + + fun Path.hasModule() = resolve("MODULE").exists() || resolve("MODULE.bazel").exists() + fun Path.hasWorkspace() = resolve("WORKSPACE").exists() || resolve("WORKSPACE.bazel").exists() + + + fun Path.run(inDirectory: Path, vararg args: String): Result = ProcessBuilder() + .command(this.toString(), *args) + .directory(inDirectory.toFile()) + .start() + .let { process -> + if (process.waitFor() == 0) { + return Result.success(process) + } + return Result.failure( + AssertionError( + """ + $this ${args.joinToString(" ")} exited ${process.exitValue()}: + stdout: + ${process.inputStream.readAllBytes().toString(UTF_8)} + stderr: + ${process.errorStream.readAllBytes().toString(UTF_8)} + """.trimIndent(), + ), + ) + } +} diff --git a/src/main/starlark/bit/rules.bzl b/src/main/starlark/bit/rules.bzl new file mode 100644 index 000000000..4191c6ecf --- /dev/null +++ b/src/main/starlark/bit/rules.bzl @@ -0,0 +1,241 @@ +"""Macros that define an sh_test target that is suitable for executing Bazel in \ +an integration test.\ +""" + +load("@bazel_skylib//lib:paths.bzl", "paths") +load("@rules_bazel_integration_test//bazel_integration_test:integration_test_utils.bzl", "integration_test_utils") + +# This was lovingly inspired by +# https://github.com/bazelbuild/rules_python/blob/main/tools/bazel_integration_test/bazel_integration_test.bzl. + +# By default, inherit the following environment variables +# HOME: Avoid "could not get the user's cache directory: $HOME is not defined" +# SUDO_ASKPASS: Support executing tests that require sudo for certain steps. +# CC: if Bazel use specific C-compiler it should be inherited by default +_DEFAULT_ENV_INHERIT = ["SUDO_ASKPASS", "HOME", "CC"] + +def _select_module_file_impl(ctx): + module_file = paths.join(ctx.attr.path, "MODULE") + module_bazel = paths.join(ctx.attr.path, "MODULE.bazel") + for file in ctx.attr.srcs.files.to_list(): + if file.path.endswith(module_file) or file.path.endswith(module_bazel): + return [DefaultInfo(files = depset([file]))] + fail("Can't find module or module.bazel in %s" % ctx.attr.module_path) + +select_module_file = rule( + implementation = _select_workspace_file_impl, + attrs = { + "srcs": attr.label( + allow_files = True, + mandatory = True, + ), + "path": attr.string( + mandatory = True, + ), + }, +) + +def bazel_integration_test( + name, + test_runner, + bazel_version = None, + bazel_binary = None, + workspace_path = None, + workspace_files = None, + module_path = None, + tags = integration_test_utils.DEFAULT_INTEGRATION_TEST_TAGS, + timeout = "long", + env = {}, + env_inherit = _DEFAULT_ENV_INHERIT, + additional_env_inherit = [], + bazel_binaries = None, + data = None, + **kwargs): + """Macro that defines a set of targets for a single Bazel integration test. + + This macro accepts an exectuable target as the test runner for the + integration test. A test runner must support two flag-value pairs: + `--bazel` and `--workspace`. The `--bazel` value specifies the + Bazel binary to use in the integration test. The `--workspace` value + specifies the path of the `WORKSPACE` file. + + If your integration test only consists of executing Bazel commands, a + default test runner is provided by the `default_test_runner` macro. + + Args: + name: name of the resulting py_test + test_runner: A `Label` for a test runner binary. (see description for + details) + bazel_version: Optional. A `string` value representing the semantic + version of Bazel to use for the integration test. If a + version is not specified, then the `bazel_binary` must + be specified. + bazel_binary: Optional. A `Label` for the Bazel binary to use for the + execution of the integration test. Most users will not + use this attribute. Use the `bazel_version` instead. + workspace_path: Optional. A `string` specifying the relative path to + the child workspace. If not specified, then it is + derived from the name. + workspace_files: Optional. A `list` of files for the child workspace. + If not specified, then it is derived from the + `workspace_path`. + tags: The Bazel tags to apply to the test declaration. + timeout: A valid Bazel timeout value. + https://docs.bazel.build/versions/main/test-encyclopedia.html#role-of-the-test-runner + env: Optional. A dictionary of `strings`. Specifies additional environment + variables to be passed to the test. + env_inherit: Optional. Override the env_inherit values passed to the + test. Only do this if you understand what needs to be + passed along. Most folks will want to use + `additional_env_inherit` to pass additional env_inherit + values. + additional_env_inherit: Optional. Specify additional `env_inherit` + values that should be passed to the test. + bazel_binaries: Optional for WORKSPACE loaded repositories. Required + for repositories that enable bzlmod. The value for this parameter + is loaded by adding + `load("@bazel_binaries//:defs.bzl", "bazel_binaries")` to your + build file. + data: Optional. A list of files to make present at test runtime. + **kwargs: additional attributes like timeout and visibility + """ + + # To support clients that have not transitioned to bzlmod, we provide a + # bazel_binaries implementation that works in that mode. If a client using + # bzlmod does not specify bazel_binaries, things will go badly for them. + if bazel_binaries == None: + bazel_binaries = struct( + label = integration_test_utils.bazel_binary_label, + ) + if bazel_binary == None and bazel_version == None: + fail("The `bazel_binary` or the `bazel_version` must be specified.") + if bazel_binary == None: + bazel_binary = bazel_binaries.label(bazel_version) + + args = [ + "--runner", + "$(location %s)" % (test_runner), + "--bazel", + "$(location %s)" % (bazel_binary), + ] + data = (data or []) + [ + test_runner, + bazel_binary, + ] + + if workspace_files != None and workspace_path == None: + fail("You must specify the `workspace_path` when specifying `workspace_files`.") + + if workspace_path != None: + # Collect the workspace files into a filegroup + if workspace_files == None: + workspace_files = integration_test_utils.glob_workspace_files(workspace_path) + + # Define a filegroup for the workspace files. + workspace_files_name = name + "_sources" + native.filegroup( + name = workspace_files_name, + srcs = workspace_files, + testonly = True, + ) + + # Find the Bazel WORKSPACE file for the target workspace. We need to + # convey the actual workspace directory to the rule. The location of + # the WORKSPACE file seems to be the best way to do this. + bazel_wksp_file_name = name + "_bazel_workspace_file" + select_module_file( + name = bazel_wksp_file_name, + srcs = workspace_files_name, + path = workspace_path, + testonly = True, + ) + + args.extend(["--workspace", "$(location :%s)" % (bazel_wksp_file_name)]) + data.extend([workspace_files_name, bazel_wksp_file_name]) + + env_inherit = env_inherit + additional_env_inherit + + env_vars_are_rootpaths = [] + for k, v in env.items(): + if v.startswith("$(rootpath") and v.index(")") == len(v) - 1: + env_vars_are_rootpaths.append(k) + if env_vars_are_rootpaths: + env["ENV_VARS_TO_ABSOLUTIFY"] = " ".join(env_vars_are_rootpaths) + + native.sh_test( + name = name, + srcs = [ + "@rules_bazel_integration_test//bazel_integration_test/private:integration_test_wrapper.sh", + ], + args = args, + data = data, + timeout = timeout, + env = env, + env_inherit = env_inherit, + tags = tags, + **kwargs + ) + +def bazel_integration_tests( + name, + test_runner, + bazel_versions = [], + workspace_path = None, + workspace_files = None, + tags = integration_test_utils.DEFAULT_INTEGRATION_TEST_TAGS, + timeout = "long", + env_inherit = _DEFAULT_ENV_INHERIT, + additional_env_inherit = [], + bazel_binaries = None, + **kwargs): + """Macro that defines a set Bazel integration tests each executed with a different version of Bazel. + + Args: + name: name of the resulting py_test + test_runner: A `Label` for a test runner binary. + workspace_path: A `string` specifying the path to the child + workspace. If not specified, then it is derived from + the name. + bazel_versions: A `list` of `string` string values representing the + semantic versions of Bazel to use for the integration + tests. + workspace_files: Optional. A `list` of files for the child workspace. + If not specified, then it is derived from the + `workspace_path`. + tags: The Bazel tags to apply to the test declaration. + timeout: A valid Bazel timeout value. + https://docs.bazel.build/versions/main/test-encyclopedia.html#role-of-the-test-runner + env_inherit: Optional. Override the env_inherit values passed to the + test. Only do this if you understand what needs to be + passed along. Most folks will want to use + `additional_env_inherit` to pass additional env_inherit + values. + additional_env_inherit: Optional. Specify additional `env_inherit` + values that should be passed to the test. + bazel_binaries: Optional for WORKSPACE loaded repositories. Required + for repositories that enable bzlmod. The value for this parameter + is loaded by adding + `load("@bazel_binaries//:defs.bzl", "bazel_binaries")` to your + build file. + **kwargs: additional attributes like timeout and visibility + """ + if bazel_versions == []: + fail("One or more Bazel versions must be specified.") + + for bazel_version in bazel_versions: + bazel_integration_test( + name = integration_test_utils.bazel_integration_test_name( + name, + bazel_version, + ), + test_runner = test_runner, + bazel_version = bazel_version, + workspace_path = workspace_path, + workspace_files = workspace_files, + tags = tags, + timeout = timeout, + env_inherit = env_inherit, + additional_env_inherit = additional_env_inherit, + bazel_binaries = bazel_binaries, + **kwargs + )