diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index f4a760c..9cd9105 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -1,2 +1,6 @@ {:linters {:unused-binding {:exclude-destructured-keys-in-fn-args true - :exclude-destructured-as true}}} + :exclude-destructured-as true} + ;; Tests in this repo use `(:require [clojure.test :refer :all])` + ;; for the conventional shorthand; silence kondo's default warning. + :refer-all {:exclude #{clojure.test}}} + :lint-as {rules-clojure.gazelle-server-test/with-temp-dir clojure.core/with-open}} diff --git a/.gitignore b/.gitignore index f525096..422d913 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ bazel-* examples/gen_srcs_bench/src/ examples/gen_srcs_bench/hyperfine.json .nrepl-port +target/ diff --git a/MODULE.bazel b/MODULE.bazel index 3a2b945..7d0c1d7 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -8,6 +8,14 @@ bazel_dep(name = "rules_jvm_external", version = "6.8") bazel_dep(name = "buildifier_prebuilt", version = "8.5.1.2", dev_dependency = True) +# Go + Gazelle for the Clojure Gazelle plugin (//gazelle). +bazel_dep(name = "rules_go", version = "0.60.0") +bazel_dep(name = "gazelle", version = "0.47.0") + +go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") +go_deps.from_file(go_mod = "//gazelle:go.mod") +use_repo(go_deps, "com_github_bazelbuild_buildtools") + maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") maven.install( name = "maven_deps", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index c2623f2..9771d39 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -18,6 +18,7 @@ "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2", + "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", @@ -30,7 +31,8 @@ "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", - "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", + "https://bcr.bazel.build/modules/bazel_features/1.36.0/MODULE.bazel": "596cb62090b039caf1cad1d52a8bc35cf188ca9a4e279a828005e7ee49a1bec3", + "https://bcr.bazel.build/modules/bazel_features/1.36.0/source.json": "279625cafa5b63cc0a8ee8448d93bc5ac1431f6000c50414051173fd22a6df3c", "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", @@ -50,6 +52,12 @@ "https://bcr.bazel.build/modules/buildifier_prebuilt/8.5.1.2/source.json": "33e11b3bf11e39cb762480a7e6ea1d24d044636135cdd8b8e74b07ebcd3b8d8b", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", + "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350", + "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a", + "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0", + "https://bcr.bazel.build/modules/gazelle/0.47.0/MODULE.bazel": "b61bb007c4efad134aa30ee7f4a8e2a39b22aa5685f005edaa022fbd1de43ebc", + "https://bcr.bazel.build/modules/gazelle/0.47.0/source.json": "aeb2e5df14b7fb298625d75d08b9c65bdb0b56014c5eb89da9e5dd0572280ae6", "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", @@ -63,6 +71,8 @@ "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", + "https://bcr.bazel.build/modules/package_metadata/0.0.5/MODULE.bazel": "ef4f9439e3270fdd6b9fd4dbc3d2f29d13888e44c529a1b243f7a31dfbc2e8e4", + "https://bcr.bazel.build/modules/package_metadata/0.0.5/source.json": "2326db2f6592578177751c3e1f74786b79382cd6008834c9d01ec865b9126a85", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", @@ -81,6 +91,8 @@ "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", "https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95", "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573", + "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", "https://bcr.bazel.build/modules/protobuf/32.1/MODULE.bazel": "89cd2866a9cb07fee9ff74c41ceace11554f32e0d849de4e23ac55515cfada4d", "https://bcr.bazel.build/modules/protobuf/32.1/source.json": "bd2664e90875c0cd755d1d9b7a103a4b027893ac8eafa3bba087557ffc244ad4", "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", @@ -106,11 +118,18 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c", "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", "https://bcr.bazel.build/modules/rules_cc/0.2.8/source.json": "85087982aca15f31307bd52698316b28faa31bd2c3095a41f456afec0131344c", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_go/0.41.0/MODULE.bazel": "55861d8e8bb0e62cbd2896f60ff303f62ffcb0eddb74ecb0e5c0cbe36fc292c8", + "https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270", + "https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd", + "https://bcr.bazel.build/modules/rules_go/0.53.0/MODULE.bazel": "a4ed760d3ac0dbc0d7b967631a9a3fd9100d28f7d9fcf214b4df87d4bfff5f9a", + "https://bcr.bazel.build/modules/rules_go/0.60.0/MODULE.bazel": "4a57ff2ffc2a3570e3c5646575c5a4b07287e91bcdac5d1f72383d51502b48cb", + "https://bcr.bazel.build/modules/rules_go/0.60.0/source.json": "1e21368c5e0c3013a110bd79a8fcff8ca46b5bcb2b561713a7273cbfcff7c464", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", @@ -149,6 +168,7 @@ "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", + "https://bcr.bazel.build/modules/rules_proto/6.0.0/MODULE.bazel": "b531d7f09f58dce456cd61b4579ce8c86b38544da75184eadaf0a7cb7966453f", "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", "https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96", @@ -181,12 +201,191 @@ "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" }, "selectedYankedVersions": {}, "moduleExtensions": { + "//:extensions.bzl%deps": { + "general": { + "bzlTransitiveDigest": "bvuoUIbvdTWkMMsvL1iBExvuWHHwNiDnzYMHROZM29E=", + "usagesDigest": "4aiDt0BRo9LfGCQL8Q0nEFdUckZDEZJJdzrUaYRp3UA=", + "recordedFileInputs": { + "@@example-simple+//deps.edn": "7076e89823651e913d5ddc808fc084a22f46cacb8bf4ef5afef0e381c3b26b81" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "deps": { + "repoRuleId": "@@//rules:tools_deps.bzl%clojure_tools_deps", + "attributes": { + "repo_name": "deps", + "aliases": [ + "dev", + "test" + ], + "clj_version": "1.11.1.1347", + "deps_edn": "@@example-simple+//:deps.edn", + "env": {}, + "root_module_name": "" + } + } + }, + "moduleExtensionMetadata": { + "useAllRepos": "REGULAR", + "reproducible": false + }, + "recordedRepoMappingEntries": [ + [ + "", + "rules_clojure", + "" + ] + ] + } + }, + "@@pybind11_bazel+//:internal_configure.bzl%internal_configure_extension": { + "general": { + "bzlTransitiveDigest": "NFQjcZF+fAvf5fDH+pqsx4JrfzP9PuHBz6S6ZutIbnw=", + "usagesDigest": "D1r3lfzMuUBFxgG8V6o0bQTLMk3GkaGOaPzw53wrwyw=", + "recordedFileInputs": { + "@@pybind11_bazel+//MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "pybind11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@pybind11_bazel+//:pybind11-BUILD.bazel", + "strip_prefix": "pybind11-2.12.0", + "urls": [ + "https://github.com/pybind/pybind11/archive/v2.12.0.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "pybind11_bazel+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_apple+//apple:apple.bzl%provisioning_profile_repository_extension": { + "general": { + "bzlTransitiveDigest": "83fAvD/IQhfPED72intPfmIdI+xpPpsLz91YBSqaU+E=", + "usagesDigest": "vsJl8Rw5NL+5Ag2wdUDoTeRF/5klkXO8545Iy7U1Q08=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_provisioning_profiles": { + "repoRuleId": "@@rules_apple+//apple/internal:local_provisioning_profiles.bzl%provisioning_profile_repository", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "bazel_tools", + "rules_cc", + "rules_cc+" + ], + [ + "rules_apple+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_apple+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_apple+", + "build_bazel_apple_support", + "apple_support+" + ], + [ + "rules_apple+", + "build_bazel_rules_swift", + "rules_swift+" + ], + [ + "rules_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_swift+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_swift+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_swift+", + "build_bazel_apple_support", + "apple_support+" + ], + [ + "rules_swift+", + "build_bazel_rules_swift", + "rules_swift+" + ], + [ + "rules_swift+", + "build_bazel_rules_swift_local_config", + "rules_swift++non_module_deps+build_bazel_rules_swift_local_config" + ] + ] + } + }, + "@@rules_apple+//apple:extensions.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "4xtddSlWIQdtVNVuvOI62fJfQVETHZCVWFvYYwQHMR4=", + "usagesDigest": "M3VqFpeTCo4qmrNKGZw0dxBHvTYDrfV3cscGzlSAhQ4=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "xctestrunner": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/google/xctestrunner/archive/b7698df3d435b6491b4b4c0f9fc7a63fbed5e3a6.tar.gz" + ], + "strip_prefix": "xctestrunner-b7698df3d435b6491b4b4c0f9fc7a63fbed5e3a6", + "sha256": "ae3a063c985a8633cb7eb566db21656f8db8eb9a0edb8c182312c7f0db53730d" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_apple+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { "bzlTransitiveDigest": "nvW/NrBXlAmiQw99EMGKkLaD2KbNp2mQDlxdfpr+0Ls=", @@ -286,7 +485,331 @@ ] ] } + }, + "@@rules_swift+//swift:extensions.bzl%non_module_deps": { + "general": { + "bzlTransitiveDigest": "6axDCXf6fQoPav8hojnUBxGA0FAMqLvtpC1cRsisCdw=", + "usagesDigest": "mhACFnrdMv9Wi0Mt67bxocJqviRkDSV+Ee5Mqdj5akA=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_apple_swift_protobuf": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-protobuf/archive/1.20.2.tar.gz" + ], + "sha256": "3fb50bd4d293337f202d917b6ada22f9548a0a0aed9d9a4d791e6fbd8a246ebb", + "strip_prefix": "swift-protobuf-1.20.2/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_protobuf/BUILD.overlay" + } + }, + "com_github_grpc_grpc_swift": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/grpc/grpc-swift/archive/1.16.0.tar.gz" + ], + "sha256": "58b60431d0064969f9679411264b82e40a217ae6bd34e17096d92cc4e47556a5", + "strip_prefix": "grpc-swift-1.16.0/", + "build_file": "@@rules_swift+//third_party:com_github_grpc_grpc_swift/BUILD.overlay" + } + }, + "com_github_apple_swift_docc_symbolkit": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-docc-symbolkit/archive/refs/tags/swift-5.10-RELEASE.tar.gz" + ], + "sha256": "de1d4b6940468ddb53b89df7aa1a81323b9712775b0e33e8254fa0f6f7469a97", + "strip_prefix": "swift-docc-symbolkit-swift-5.10-RELEASE", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_docc_symbolkit/BUILD.overlay" + } + }, + "com_github_apple_swift_nio": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio/archive/2.42.0.tar.gz" + ], + "sha256": "e3304bc3fb53aea74a3e54bd005ede11f6dc357117d9b1db642d03aea87194a0", + "strip_prefix": "swift-nio-2.42.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_http2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-http2/archive/1.26.0.tar.gz" + ], + "sha256": "f0edfc9d6a7be1d587e5b403f2d04264bdfae59aac1d74f7d974a9022c6d2b25", + "strip_prefix": "swift-nio-http2-1.26.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_http2/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_transport_services": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-transport-services/archive/1.15.0.tar.gz" + ], + "sha256": "f3498dafa633751a52b9b7f741f7ac30c42bcbeb3b9edca6d447e0da8e693262", + "strip_prefix": "swift-nio-transport-services-1.15.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_transport_services/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_extras": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-extras/archive/1.4.0.tar.gz" + ], + "sha256": "4684b52951d9d9937bb3e8ccd6b5daedd777021ef2519ea2f18c4c922843b52b", + "strip_prefix": "swift-nio-extras-1.4.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_extras/BUILD.overlay" + } + }, + "com_github_apple_swift_log": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-log/archive/1.4.4.tar.gz" + ], + "sha256": "48fe66426c784c0c20031f15dc17faf9f4c9037c192bfac2f643f65cb2321ba0", + "strip_prefix": "swift-log-1.4.4/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_log/BUILD.overlay" + } + }, + "com_github_apple_swift_nio_ssl": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-nio-ssl/archive/2.23.0.tar.gz" + ], + "sha256": "4787c63f61dd04d99e498adc3d1a628193387e41efddf8de19b8db04544d016d", + "strip_prefix": "swift-nio-ssl-2.23.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_nio_ssl/BUILD.overlay" + } + }, + "com_github_apple_swift_collections": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-collections/archive/1.0.4.tar.gz" + ], + "sha256": "d9e4c8a91c60fb9c92a04caccbb10ded42f4cb47b26a212bc6b39cc390a4b096", + "strip_prefix": "swift-collections-1.0.4/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_collections/BUILD.overlay" + } + }, + "com_github_apple_swift_atomics": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/apple/swift-atomics/archive/1.1.0.tar.gz" + ], + "sha256": "1bee7f469f7e8dc49f11cfa4da07182fbc79eab000ec2c17bfdce468c5d276fb", + "strip_prefix": "swift-atomics-1.1.0/", + "build_file": "@@rules_swift+//third_party:com_github_apple_swift_atomics/BUILD.overlay" + } + }, + "build_bazel_rules_swift_index_import": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@rules_swift+//third_party:build_bazel_rules_swift_index_import/BUILD.overlay", + "canonical_id": "index-import-5.8", + "urls": [ + "https://github.com/MobileNativeFoundation/index-import/releases/download/5.8.0.1/index-import.tar.gz" + ], + "sha256": "28c1ffa39d99e74ed70623899b207b41f79214c498c603915aef55972a851a15" + } + }, + "build_bazel_rules_swift_local_config": { + "repoRuleId": "@@rules_swift+//swift/internal:swift_autoconfiguration.bzl%swift_autoconfiguration", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_swift+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_swift+", + "build_bazel_rules_swift", + "rules_swift+" + ] + ] + } } }, - "facts": {} + "facts": { + "@@rules_go+//go:extensions.bzl%go_sdk": { + "1.25.0": { + "aix_ppc64": [ + "go1.25.0.aix-ppc64.tar.gz", + "e5234a7dac67bc86c528fe9752fc9d63557918627707a733ab4cac1a6faed2d4" + ], + "darwin_amd64": [ + "go1.25.0.darwin-amd64.tar.gz", + "5bd60e823037062c2307c71e8111809865116714d6f6b410597cf5075dfd80ef" + ], + "darwin_arm64": [ + "go1.25.0.darwin-arm64.tar.gz", + "544932844156d8172f7a28f77f2ac9c15a23046698b6243f633b0a0b00c0749c" + ], + "dragonfly_amd64": [ + "go1.25.0.dragonfly-amd64.tar.gz", + "5ed3cf9a810a1483822538674f1336c06b51aa1b94d6d545a1a0319a48177120" + ], + "freebsd_386": [ + "go1.25.0.freebsd-386.tar.gz", + "abea5d5c6697e6b5c224731f2158fe87c602996a2a233ac0c4730cd57bf8374e" + ], + "freebsd_amd64": [ + "go1.25.0.freebsd-amd64.tar.gz", + "86e6fe0a29698d7601c4442052dac48bd58d532c51cccb8f1917df648138730b" + ], + "freebsd_arm": [ + "go1.25.0.freebsd-arm.tar.gz", + "d90b78e41921f72f30e8bbc81d9dec2cff7ff384a33d8d8debb24053e4336bfe" + ], + "freebsd_arm64": [ + "go1.25.0.freebsd-arm64.tar.gz", + "451d0da1affd886bfb291b7c63a6018527b269505db21ce6e14724f22ab0662e" + ], + "freebsd_riscv64": [ + "go1.25.0.freebsd-riscv64.tar.gz", + "7b565f76bd8bda46549eeaaefe0e53b251e644c230577290c0f66b1ecdb3cdbe" + ], + "illumos_amd64": [ + "go1.25.0.illumos-amd64.tar.gz", + "b1e1fdaab1ad25aa1c08d7a36c97d45d74b98b89c3f78c6d2145f77face54a2c" + ], + "linux_386": [ + "go1.25.0.linux-386.tar.gz", + "8c602dd9d99bc9453b3995d20ce4baf382cc50855900a0ece5de9929df4a993a" + ], + "linux_amd64": [ + "go1.25.0.linux-amd64.tar.gz", + "2852af0cb20a13139b3448992e69b868e50ed0f8a1e5940ee1de9e19a123b613" + ], + "linux_arm64": [ + "go1.25.0.linux-arm64.tar.gz", + "05de75d6994a2783699815ee553bd5a9327d8b79991de36e38b66862782f54ae" + ], + "linux_armv6l": [ + "go1.25.0.linux-armv6l.tar.gz", + "a5a8f8198fcf00e1e485b8ecef9ee020778bf32a408a4e8873371bfce458cd09" + ], + "linux_loong64": [ + "go1.25.0.linux-loong64.tar.gz", + "cab86b1cf761b1cb3bac86a8877cfc92e7b036fc0d3084123d77013d61432afc" + ], + "linux_mips": [ + "go1.25.0.linux-mips.tar.gz", + "d66b6fb74c3d91b9829dc95ec10ca1f047ef5e89332152f92e136cf0e2da5be1" + ], + "linux_mips64": [ + "go1.25.0.linux-mips64.tar.gz", + "4082e4381a8661bc2a839ff94ba3daf4f6cde20f8fb771b5b3d4762dc84198a2" + ], + "linux_mips64le": [ + "go1.25.0.linux-mips64le.tar.gz", + "70002c299ec7f7175ac2ef673b1b347eecfa54ae11f34416a6053c17f855afcc" + ], + "linux_mipsle": [ + "go1.25.0.linux-mipsle.tar.gz", + "b00a3a39eff099f6df9f1c7355bf28e4589d0586f42d7d4a394efb763d145a73" + ], + "linux_ppc64": [ + "go1.25.0.linux-ppc64.tar.gz", + "df166f33bd98160662560a72ff0b4ba731f969a80f088922bddcf566a88c1ec1" + ], + "linux_ppc64le": [ + "go1.25.0.linux-ppc64le.tar.gz", + "0f18a89e7576cf2c5fa0b487a1635d9bcbf843df5f110e9982c64df52a983ad0" + ], + "linux_riscv64": [ + "go1.25.0.linux-riscv64.tar.gz", + "c018ff74a2c48d55c8ca9b07c8e24163558ffec8bea08b326d6336905d956b67" + ], + "linux_s390x": [ + "go1.25.0.linux-s390x.tar.gz", + "34e5a2e19f2292fbaf8783e3a241e6e49689276aef6510a8060ea5ef54eee408" + ], + "netbsd_386": [ + "go1.25.0.netbsd-386.tar.gz", + "f8586cdb7aa855657609a5c5f6dbf523efa00c2bbd7c76d3936bec80aa6c0aba" + ], + "netbsd_amd64": [ + "go1.25.0.netbsd-amd64.tar.gz", + "ae8dc1469385b86a157a423bb56304ba45730de8a897615874f57dd096db2c2a" + ], + "netbsd_arm": [ + "go1.25.0.netbsd-arm.tar.gz", + "1ff7e4cc764425fc9dd6825eaee79d02b3c7cafffbb3691687c8d672ade76cb7" + ], + "netbsd_arm64": [ + "go1.25.0.netbsd-arm64.tar.gz", + "e1b310739f26724216aa6d7d7208c4031f9ff54c9b5b9a796ddc8bebcb4a5f16" + ], + "openbsd_386": [ + "go1.25.0.openbsd-386.tar.gz", + "4802a9b20e533da91adb84aab42e94aa56cfe3e5475d0550bed3385b182e69d8" + ], + "openbsd_amd64": [ + "go1.25.0.openbsd-amd64.tar.gz", + "c016cd984bebe317b19a4f297c4f50def120dc9788490540c89f28e42f1dabe1" + ], + "openbsd_arm": [ + "go1.25.0.openbsd-arm.tar.gz", + "a1e31d0bf22172ddde42edf5ec811ef81be43433df0948ece52fecb247ccfd8d" + ], + "openbsd_arm64": [ + "go1.25.0.openbsd-arm64.tar.gz", + "343ea8edd8c218196e15a859c6072d0dd3246fbbb168481ab665eb4c4140458d" + ], + "openbsd_ppc64": [ + "go1.25.0.openbsd-ppc64.tar.gz", + "694c14da1bcaeb5e3332d49bdc2b6d155067648f8fe1540c5de8f3cf8e157154" + ], + "openbsd_riscv64": [ + "go1.25.0.openbsd-riscv64.tar.gz", + "aa510ad25cf54c06cd9c70b6d80ded69cb20188ac6e1735655eef29ff7e7885f" + ], + "plan9_386": [ + "go1.25.0.plan9-386.tar.gz", + "46f8cef02086cf04bf186c5912776b56535178d4cb319cd19c9fdbdd29231986" + ], + "plan9_amd64": [ + "go1.25.0.plan9-amd64.tar.gz", + "29b34391d84095e44608a228f63f2f88113a37b74a79781353ec043dfbcb427b" + ], + "plan9_arm": [ + "go1.25.0.plan9-arm.tar.gz", + "0a047107d13ebe7943aaa6d54b1d7bbd2e45e68ce449b52915a818da715799c2" + ], + "solaris_amd64": [ + "go1.25.0.solaris-amd64.tar.gz", + "9977f9e4351984364a3b2b78f8b88bfd1d339812356d5237678514594b7d3611" + ], + "windows_386": [ + "go1.25.0.windows-386.zip", + "df9f39db82a803af0db639e3613a36681ab7a42866b1384b3f3a1045663961a7" + ], + "windows_amd64": [ + "go1.25.0.windows-amd64.zip", + "89efb4f9b30812eee083cc1770fdd2913c14d301064f6454851428f9707d190b" + ], + "windows_arm64": [ + "go1.25.0.windows-arm64.zip", + "27bab004c72b3d7bd05a69b6ec0fc54a309b4b78cc569dd963d8b3ec28bfdb8c" + ] + } + } + } } diff --git a/gazelle/BUILD.bazel b/gazelle/BUILD.bazel new file mode 100644 index 0000000..854ffc3 --- /dev/null +++ b/gazelle/BUILD.bazel @@ -0,0 +1,48 @@ +load("@gazelle//:def.bzl", "gazelle_binary") +load("@rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "gazelle_lib", + srcs = [ + "configure.go", + "generate.go", + "lang.go", + "resolve.go", + ], + importpath = "github.com/griffinbank/rules_clojure/gazelle", + visibility = ["//visibility:public"], + deps = [ + "//gazelle/clojureconfig", + "//gazelle/clojureparser", + "@gazelle//config:go_default_library", + "@gazelle//label:go_default_library", + "@gazelle//language:go_default_library", + "@gazelle//repo:go_default_library", + "@gazelle//resolve:go_default_library", + "@gazelle//rule:go_default_library", + ], +) + +go_test( + name = "gazelle_test", + srcs = [ + "configure_test.go", + "generate_test.go", + "resolve_test.go", + ], + embed = [":gazelle_lib"], + deps = [ + "//gazelle/clojureconfig", + "//gazelle/clojureparser", + "@com_github_bazelbuild_buildtools//build:go_default_library", + "@gazelle//config:go_default_library", + "@gazelle//label:go_default_library", + "@gazelle//rule:go_default_library", + ], +) + +gazelle_binary( + name = "gazelle_bin", + languages = [":gazelle_lib"], + visibility = ["//visibility:public"], +) diff --git a/gazelle/clojureconfig/BUILD.bazel b/gazelle/clojureconfig/BUILD.bazel new file mode 100644 index 0000000..b1cbaf4 --- /dev/null +++ b/gazelle/clojureconfig/BUILD.bazel @@ -0,0 +1,14 @@ +load("@rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "clojureconfig", + srcs = ["config.go"], + importpath = "github.com/griffinbank/rules_clojure/gazelle/clojureconfig", + visibility = ["//gazelle:__subpackages__"], +) + +go_test( + name = "clojureconfig_test", + srcs = ["config_test.go"], + embed = [":clojureconfig"], +) diff --git a/gazelle/clojureconfig/config.go b/gazelle/clojureconfig/config.go new file mode 100644 index 0000000..e150a47 --- /dev/null +++ b/gazelle/clojureconfig/config.go @@ -0,0 +1,132 @@ +// Package clojureconfig collects Gazelle directives for the Clojure plugin. +// It holds raw directive values per package (rel → kv-map) and supports the +// parent-fallback walk Gazelle's per-package config model implies. Semantic +// interpretation of resolved values — basis assembly, label generation, +// effective rule shape — belongs in the Clojure server; the trivial value- +// string normalization needed for directive consumption (alias parsing, +// trim-empty) lives here because every Go-side caller needs it. +package clojureconfig + +import ( + "path" + "strings" +) + +// Directive name constants used in BUILD file comments. +const ( + ClojureExtensionDirective = "clojure_enabled" + ClojureDepsEdn = "clojure_deps_edn" + ClojureDepsRepo = "clojure_deps_repo" + ClojureAliases = "clojure_aliases" + + // rootModuleName is auto-discovered from MODULE.bazel; it isn't a + // user-facing directive but is stored under the same map. + rootModuleName = "_root_module_name" +) + +// Configs maps package-relative paths to their raw directive values. +// Lookup goes through Effective() which walks ancestors for inheritance. +type Configs map[string]map[string]string + +// Effective returns the value for `key` at `rel`, falling back to ancestor +// rels (parent dirs) and finally `dflt` when no override was set. +func (cs Configs) Effective(rel, key, dflt string) string { + for { + if v, ok := cs[rel][key]; ok { + return v + } + if rel == "" { + return dflt + } + rel = path.Dir(rel) + if rel == "." { + rel = "" + } + } +} + +// ExtensionEnabled returns whether the Clojure extension is active for rel, +// honouring per-package `# gazelle:clojure_enabled false` overrides. +// Defaults to true at the root. +func (cs Configs) ExtensionEnabled(rel string) bool { + switch cs.Effective(rel, ClojureExtensionDirective, "true") { + case "false": + return false + default: + return true + } +} + +// DepsRepo returns the deps repo tag for rel — default "@deps" at the root, +// overridden by `# gazelle:clojure_deps_repo @other_deps` in any ancestor. +func (cs Configs) DepsRepo(rel string) string { + return cs.Effective(rel, ClojureDepsRepo, "@deps") +} + +// DepsEdn returns the workspace-relative deps.edn path captured at the root +// during the initial Configure pass. Auto-discovered if no directive set. +func (cs Configs) DepsEdn() string { + return cs.Effective("", ClojureDepsEdn, "") +} + +// RootModuleName returns the bzlmod module(name=...) value, captured during +// the root Configure pass by reading MODULE.bazel. Used to canonicalize +// self-referencing labels (`@//foo` → `@@//foo`) so generated rules +// agree with the apparent-name view Bazel exposes to consumers. +func (cs Configs) RootModuleName() string { + return cs.Effective("", rootModuleName, "") +} + +// SetRootModuleName stashes the discovered bzlmod root module name on the +// root config. +func (cs Configs) SetRootModuleName(name string) { + if _, ok := cs[""]; !ok { + cs[""] = map[string]string{} + } + cs[""][rootModuleName] = name +} + +// Aliases returns the parsed alias list from the root-level +// `# gazelle:clojure_aliases` directive. Per-package alias overrides are +// not supported (aliases feed the basis, which is computed once at init). +func (cs Configs) Aliases() []string { + return ParseAliases(cs.Effective("", ClojureAliases, "")) +} + +// Set stores a raw directive value on `rel`. Empty values are treated as +// "unset" so a child config doesn't accidentally override its parent with +// the zero value — Effective() falls through to the next ancestor. +func (cs Configs) Set(rel, key, value string) { + if value == "" { + return + } + if _, ok := cs[rel]; !ok { + cs[rel] = map[string]string{} + } + cs[rel][key] = value +} + +// AllDirectives returns the directive names Gazelle should recognize from +// `# gazelle: ` comments in BUILD files. +func AllDirectives() []string { + return []string{ + ClojureExtensionDirective, + ClojureDepsEdn, + ClojureDepsRepo, + ClojureAliases, + } +} + +// ParseAliases splits a comma-separated alias string, trims whitespace, +// and drops empty entries. strings.Split("", ",") returns [""], and a +// naive split of "dev, test" leaves a leading space on the second entry +// — both yield malformed keywords on the Clojure side. +func ParseAliases(s string) []string { + out := []string{} + for _, raw := range strings.Split(s, ",") { + if trimmed := strings.TrimSpace(raw); trimmed != "" { + out = append(out, trimmed) + } + } + return out +} diff --git a/gazelle/clojureconfig/config_test.go b/gazelle/clojureconfig/config_test.go new file mode 100644 index 0000000..0dcca38 --- /dev/null +++ b/gazelle/clojureconfig/config_test.go @@ -0,0 +1,97 @@ +package clojureconfig + +import ( + "reflect" + "testing" +) + +func TestParseAliases(t *testing.T) { + cases := []struct { + name string + in string + want []string + }{ + {name: "empty", in: "", want: []string{}}, + {name: "single", in: "dev", want: []string{"dev"}}, + {name: "two", in: "dev,test", want: []string{"dev", "test"}}, + {name: "trims whitespace", in: " dev , test ", want: []string{"dev", "test"}}, + {name: "drops empty entries", in: ",,dev,,test,", want: []string{"dev", "test"}}, + {name: "all empty", in: ",,,", want: []string{}}, + {name: "colon-prefixed kept verbatim", in: ":dev", want: []string{":dev"}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := ParseAliases(tc.in) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("ParseAliases(%q) = %v; want %v", tc.in, got, tc.want) + } + }) + } +} + +func TestEffectiveWalksParentChain(t *testing.T) { + cs := Configs{ + "": {ClojureDepsRepo: "@root"}, + "a": {ClojureDepsRepo: "@a"}, + "a/b/c": {ClojureDepsRepo: "@deep"}, + } + cases := map[string]string{ + "": "@root", + "a": "@a", + "a/b": "@a", // a/b unset → a + "a/b/c": "@deep", // exact match + "a/b/c/d": "@deep", // child of c → deep + "x/y/z": "@root", // unrelated → root default + } + for rel, want := range cases { + t.Run(rel, func(t *testing.T) { + got := cs.DepsRepo(rel) + if got != want { + t.Errorf("DepsRepo(%q) = %q; want %q", rel, got, want) + } + }) + } +} + +func TestEffectiveDefaultsWhenNothingSet(t *testing.T) { + cs := Configs{} + if got := cs.DepsRepo("anything"); got != "@deps" { + t.Errorf("DepsRepo on empty Configs = %q; want @deps default", got) + } +} + +func TestExtensionEnabledFalseSticks(t *testing.T) { + cs := Configs{} + if !cs.ExtensionEnabled("") { + t.Error("default ExtensionEnabled = false; want true") + } + cs.Set("a", ClojureExtensionDirective, "false") + if cs.ExtensionEnabled("a") { + t.Error("ExtensionEnabled(a) after false override = true") + } + // Child inherits parent's false. + if cs.ExtensionEnabled("a/b") { + t.Error("child ExtensionEnabled = true; want inherited false") + } + // Sibling unaffected. + if !cs.ExtensionEnabled("other") { + t.Error("sibling ExtensionEnabled = false; want default true") + } +} + +func TestAliasesParsesRootDirective(t *testing.T) { + cs := Configs{} + cs.Set("", ClojureAliases, "dev,test") + got := cs.Aliases() + if !reflect.DeepEqual(got, []string{"dev", "test"}) { + t.Errorf("Aliases() = %v; want [dev test]", got) + } +} + +func TestSetRootModuleName(t *testing.T) { + cs := Configs{} + cs.SetRootModuleName("foo") + if got := cs.RootModuleName(); got != "foo" { + t.Errorf("RootModuleName = %q; want foo", got) + } +} diff --git a/gazelle/clojureparser/BUILD.bazel b/gazelle/clojureparser/BUILD.bazel new file mode 100644 index 0000000..42254cd --- /dev/null +++ b/gazelle/clojureparser/BUILD.bazel @@ -0,0 +1,17 @@ +load("@rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "clojureparser", + srcs = ["parser.go"], + importpath = "github.com/griffinbank/rules_clojure/gazelle/clojureparser", + visibility = ["//gazelle:__subpackages__"], +) + +go_test( + name = "clojureparser_test", + srcs = [ + "lifecycle_test.go", + "parser_test.go", + ], + embed = [":clojureparser"], +) diff --git a/gazelle/clojureparser/lifecycle_test.go b/gazelle/clojureparser/lifecycle_test.go new file mode 100644 index 0000000..c4d3d8a --- /dev/null +++ b/gazelle/clojureparser/lifecycle_test.go @@ -0,0 +1,117 @@ +package clojureparser + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" +) + +// stubScript creates a temporary executable shell script with the given +// content and returns its path. The script is removed on test cleanup. +func stubScript(t *testing.T, content string) string { + t.Helper() + if _, err := exec.LookPath("sh"); err != nil { + t.Skip("sh not on PATH") + } + path := filepath.Join(t.TempDir(), "stub.sh") + if err := os.WriteFile(path, []byte("#!/bin/sh\n"+content), 0755); err != nil { + t.Fatal(err) + } + return path +} + +func TestRunnerExchangeMarksDeadOnEOF(t *testing.T) { + // Script exits immediately after reading one line. The first exchange + // receives nothing back → unexpected EOF, runner.dead = true. + stub := stubScript(t, "read first\n") + runner, err := New(stub) + if err != nil { + t.Fatalf("New: %v", err) + } + defer runner.Shutdown() + + if _, err := runner.Init(InitRequest{DepsEdnPath: "/dev/null"}); err == nil { + t.Fatal("first Init expected error from EOF on dying subprocess") + } + if !runner.dead { + t.Error("runner.dead = false after exchange failure; want true") + } + + // Second call must short-circuit on the dead flag, not race the corpse. + _, err = runner.Parse(ParseRequest{Dir: "x"}) + if err != ErrRunnerDead { + t.Errorf("second call err = %v; want ErrRunnerDead", err) + } +} + +func TestRunnerShutdownIdempotent(t *testing.T) { + stub := stubScript(t, "cat\n") + runner, err := New(stub) + if err != nil { + t.Fatalf("New: %v", err) + } + runner.Shutdown() + runner.Shutdown() // must not panic, deadlock, or double-close +} + +func TestRunnerShutdownOnNilReceiver(t *testing.T) { + var r *Runner + r.Shutdown() // must not panic +} + +func TestInitRejectsMalformedJSON(t *testing.T) { + stub := stubScript(t, "read line\n"+`printf 'not json at all\n'`+"\n") + runner, err := New(stub) + if err != nil { + t.Fatalf("New: %v", err) + } + defer runner.Shutdown() + _, err = runner.Init(InitRequest{DepsEdnPath: "/dev/null"}) + if err == nil { + t.Fatal("Init accepted malformed JSON; want error") + } + if !strings.Contains(err.Error(), "malformed init response") { + t.Errorf("err = %q; want 'malformed init response' marker", err.Error()) + } +} + +func TestInitRejectsErrorEnvelope(t *testing.T) { + stub := stubScript(t, "read line\n"+`printf '%s\n' '{"type":"error","message":"boom"}'`+"\n") + runner, err := New(stub) + if err != nil { + t.Fatalf("New: %v", err) + } + defer runner.Shutdown() + _, err = runner.Init(InitRequest{DepsEdnPath: "/dev/null"}) + if err == nil { + t.Fatal("Init accepted error envelope; want error") + } + if !strings.Contains(err.Error(), "boom") { + t.Errorf("err = %q; want 'boom' message", err.Error()) + } +} + +func TestRunnerExchangeRoundTrip(t *testing.T) { + // Script echoes a canned response after reading one request. + resp := `{"type":"init","dep_ns_labels":{},"deps_bazel":{},"ignore_paths":[],"source_paths":[]}` + stub := stubScript(t, "read line\n"+`printf '%s\n' '`+resp+`'`+"\n") + runner, err := New(stub) + if err != nil { + t.Fatalf("New: %v", err) + } + defer runner.Shutdown() + + got, err := runner.Init(InitRequest{DepsEdnPath: "/dev/null"}) + if err != nil { + t.Fatalf("Init: %v", err) + } + if got.Type != MsgTypeInit { + t.Errorf("Type = %q; want %q", got.Type, MsgTypeInit) + } + + // Give the stub a moment to exit so the next test doesn't see a zombie. + time.Sleep(50 * time.Millisecond) +} diff --git a/gazelle/clojureparser/parser.go b/gazelle/clojureparser/parser.go new file mode 100644 index 0000000..1a68773 --- /dev/null +++ b/gazelle/clojureparser/parser.go @@ -0,0 +1,425 @@ +// Package clojureparser is the Go client for the long-running Clojure +// parser subprocess. It defines the JSON-line wire protocol (request and +// response types) and Runner, which owns the subprocess lifecycle: start, +// Init, Parse, Shutdown. Calls serialize on an internal mutex; any I/O +// error marks the runner dead so subsequent calls short-circuit instead +// of racing a corpse. +package clojureparser + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "os" + "os/exec" + "sync" + "time" +) + +// shutdownTimeout caps how long Shutdown waits for the subprocess to exit +// after stdin close. If exceeded, the process is killed. +const shutdownTimeout = 10 * time.Second + +// ErrRunnerDead is returned by Init/Parse after any prior send/receive +// failure to short-circuit subsequent calls that would race a dead subprocess. +var ErrRunnerDead = errors.New("clojureparser: runner is dead (prior failure)") + +// Request/response types for the JSON-line protocol with the Clojure parser subprocess. + +// Message type discriminators. Keep in sync with handle-request in +// src/rules_clojure/gazelle_server.clj. +const ( + MsgTypeInit = "init" + MsgTypeParse = "parse" + MsgTypeError = "error" +) + +// Platform is one of the values reported by the parser. Promoting from a +// plain string aids self-documentation in resolve.go: `ns.Requires[platform]` +// vs `ns.Requires[string]` makes it clear what the keys are. +type Platform = string + +// Platform enum values. Mirror gen_build.clj's clj-path? / cljs-path? / etc. +const ( + PlatformClj Platform = "clj" + PlatformCljs Platform = "cljs" + PlatformJs Platform = "js" +) + +// InitRequest is the one-shot configuration sent at startup. The Clojure +// server reads deps.edn, resolves transitive Maven coords against +// RepositoryDir, and replies with InitResponse. +type InitRequest struct { + Type string `json:"type"` + DepsEdnPath string `json:"deps_edn_path"` // absolute path to deps.edn + RepositoryDir string `json:"repository_dir"` // local Maven repo (~/.m2/repository) + DepsRepoTag string `json:"deps_repo_tag"` // e.g. "@deps" — prefix for resolved labels + RootModuleName string `json:"root_module_name"` // bzlmod module(name=...) for self-label canonicalization + Aliases []string `json:"aliases"` // deps.edn :aliases to activate +} + +// InitResponse carries the resolved dep graph and configured paths. All +// slice fields are emitted as JSON arrays (never null) so the Go caller can +// range over them without a nil check. +// +// Note on DepNsLabels: the server emits a map keyed by platform string; +// individual platform keys may be absent when the basis has no jars +// providing that platform. resolve.go's lookup is guarded with a comma-ok +// so a missing key becomes a no-op rather than a panic, but downstream +// require-resolution will silently fail to find the dep — review the basis +// if a require that should resolve doesn't. +type InitResponse struct { + Type string `json:"type"` + // DepNsLabels maps platform → namespace-string → Bazel-label. Platform + // keys correspond to MsgType-style strings ("clj", "cljs"); inner keys + // are namespace symbols rendered as strings ("foo.bar"). + DepNsLabels map[string]map[string]string `json:"dep_ns_labels"` + // DepsBazel mirrors the :bazel key from deps.edn (allows per-target + // override of generated deps). Other fields under :bazel are decoded + // and silently ignored — only :deps is consumed by resolve.go. + DepsBazel DepsBazel `json:"deps_bazel"` + // IgnorePaths are package-relative paths Gazelle should not generate + // rules for. SourcePaths is the same shape — paths included for + // generation, mirroring deps.edn :paths after alias merging. + IgnorePaths []string `json:"ignore_paths"` + SourcePaths []string `json:"source_paths"` +} + +// DepsBazel carries the per-target dep overrides from deps.edn's :bazel +// map. The Clojure server emits the whole :bazel map; this type only +// names the field Resolve consumes (Deps), so other keys decode but stay +// inert. Resolve adds the entries in Deps[