Skip to content

Commit

Permalink
script embedded
Browse files Browse the repository at this point in the history
  • Loading branch information
anna-skrodzka committed Nov 26, 2024
1 parent d9718af commit 38d5e78
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 75 deletions.
15 changes: 14 additions & 1 deletion rules/scalafix/BUILD
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
load("//rules/scalafix:scalafix_runner.bzl", "scalafix_runner")
load("@rules_scala3//deps:scala_deps.bzl", "scala_deps")

filegroup(
name = "dependencies",
srcs = ["Dependencies.scala"],
visibility = ["//visibility:public"],
)

scala_deps(
name = "scala_deps",
src = "//rules/scalafix:dependencies",
dependencies = "rules_scala3.rules.scalafix.Dependencies",
)

scalafix_runner(
name = "run_scalafix",
toolchain = "//:scalafix_toolchain",
targets = ["//deps:deps"],
)
)
162 changes: 88 additions & 74 deletions rules/scalafix/scalafix_runner.bzl
Original file line number Diff line number Diff line change
@@ -1,90 +1,104 @@
def _scalafix_runner_impl(ctx):

toolchain = ctx.attr.toolchain.label
scalafix_opts = ctx.attr.opts or ""

targets = " ".join(['"%s"' % t.label for t in ctx.attr.targets])
excluded_targets = " ".join(['"%s"' % ("-" + et.label) for et in ctx.attr.excluded_targets])

# Dynamically generate the script content
script_content = """#!/usr/bin/env bash
set -e
# Navigate to the Bazel workspace
workspace="$BUILD_WORKSPACE_DIRECTORY"
cd "$workspace"
toolchain="%s"
scalafix_opts="%s"
targets=(%s)
excluded_targets=(%s)
# Variables
toolchain="{toolchain}"
opts="{opts}"
targets=({targets})
excluded_targets=({excluded_targets})
# Filter targets
echo "Targets: ${targets[*]}"
echo "Excluded Targets: ${excluded_targets[*]}"
filtered_targets=$(
bazel query \\
"kind('scala(_binary|_library|_test|js_library)', set(${targets[@]}) except set(${excluded_targets[@]}))" \\
2>/dev/null
)
echo "Filtering targets..."
readarray -t filtered_targets < <(bazel query \
"kind('scala(_binary|_library|_test|js_library)', set(${targets[*]}) except set(${excluded_targets[*]}))" --output=label 2>/dev/null)
if [[ -z "$filtered_targets" ]]; then
echo "No matching targets found."
exit 0
if [[ ${#filtered_targets[@]} -eq 0 ]]; then
echo "No valid targets found to build."
exit 1
fi
# Debug filtered targets
echo "Filtered targets: ${filtered_targets[@]}"
# Build targets
build_cmd="bazel build --extra_toolchains='$toolchain' -- ${filtered_targets[@]}"
echo "Building targets..."
bazel build --extra_toolchains="$toolchain" -- $filtered_targets
echo "Command: $build_cmd"
if ! eval "$build_cmd"; then
echo "BUILD FAILED, FIX AND TRY AGAIN"
kill -INT $$
fi
# Run scalafix
exec_root=$(bazel info execution_root 2>/dev/null)
for target in $filtered_targets; do
echo "Running scalafix for $target..."
files=$(
bazel query "$target" --output=streamed_jsonproto 2>/dev/null | \\
jq -r '.rule.attribute[]? | select(.name=="srcs" and .stringListValue!=null) | .stringListValue[]'
)
if [[ -z "$files" ]]; then
continue
fi
# Get the source files for the target
files=$(
bazel query "kind('source file', deps($target))" --output=label 2>/dev/null | \
sed 's|^//|./|' | sed 's|:|/|'
)
if [[ -z "$files" ]]; then
echo "No source files found for $target"
continue
fi
scalac_opts=$(
bazel query "$toolchain" --output=streamed_jsonproto 2>/dev/null | \
jq -r '.rule.attribute[] | select(.name=="global_scalacopts" and .stringListValue!=null) | .stringListValue[]'
)
required_options="-Wunused:all"
if [[ "$scalac_opts" != *"$required_options"* ]]; then
scalac_opts="$scalac_opts $required_options"
fi
scala_version=$(
bazel cquery "$target" --output=starlark --starlark:expr \\
'providers(target).get("java").scala_info.toolchain.scala_version' 2>/dev/null
)
classpath="--classpath $exec_root/$(bazel cquery "$target.jar" --output files 2>/dev/null)"
sourceroot="--sourceroot $workspace"
scalafix_cmd="scalafix ${scalafix_opts//:/ } --scala-version $scala_version $sourceroot $classpath ${scalac_opts[*]/#/--scalac-options } $files"
echo "$scalafix_cmd"
eval "$scalafix_cmd"
exec_root="$(bazel info execution_root 2>/dev/null)"
toolchain_impl="$(
bazel query "$toolchain" --output=streamed_jsonproto 2>/dev/null |
jq -r '.rule.attribute[] | select(.name=="toolchain" and .explicitlySpecified==true) | .stringValue'
)"
for target in "${filtered_targets[@]}"; do
target_json="$(bazel query "$target" --output=streamed_jsonproto 2>/dev/null)"
readarray -t files < <(
echo "$target_json" |
jq -r '.rule.attribute[]? | select(.name=="srcs" and .stringListValue!=null) | .stringListValue[]' |
while read -r source; do
printf -- "--files %s\n" "$(bazel query "$source" --output location 2>/dev/null)"
done
)
if [[ ${#files[@]} -eq 0 ]]; then
continue
fi
if echo "$target_json" |
jq -e '.rule.attribute[] | select(.name=="scala" and .explicitlySpecified==true)' >/dev/null; then
actual_toolchain="$(echo "$target_json" | jq -r '.rule.attribute[] | select(.name=="scala") | .stringValue')"
else
actual_toolchain=$toolchain_impl
fi
readarray -t scalac_opts < <(
# if 'enable_semanticdb = True' toolchain adds this under the hood
if bazel query "$toolchain_impl" --output streamed_jsonproto 2>/dev/null |
jq -e '.rule.attribute[] | select(.name=="enable_semanticdb" and .stringValue=="true")' >/dev/null; then
echo "-Xsemanticdb"
fi
# scalacopts from toolchain
bazel query "$actual_toolchain" --output streamed_jsonproto 2>/dev/null |
jq -r '.rule.attribute[] | select(.name=="global_scalacopts" and .stringListValue!=null) | .stringListValue[]'
# scalacopts passed when defining the target
echo "$target_json" |
jq -r '.rule.attribute[] | select(.name=="scalacopts" and .stringListValue!=null) | .stringListValue[]'
)
scala_version="$(
bazel cquery "$target" --output starlark --starlark:expr \
'providers(target).get("java").scala_info.toolchain.scala_version' 2>/dev/null
)"
cs="--classpath $exec_root/$(bazel cquery "$target.jar" --output files 2>/dev/null)"
sr="--sourceroot $(bazel info workspace 2>/dev/null)"
scalafix_cmd="scalafix ${scalafix_opts//:/ } --scala-version $scala_version $sr $cs ${scalac_opts[*]/#/--scalac-options } ${files[*]%%:*}"
echo "\nTrying to fix $target"
echo "Command: $scalafix_cmd"
eval "$scalafix_cmd"
done
""" % (toolchain, scalafix_opts, targets, excluded_targets)
""".replace(
"{toolchain}", str(ctx.attr.toolchain.label)
).replace(
"{opts}", ctx.attr.opts
).replace(
"{targets}", " ".join(['"%s"' % t for t in ctx.attr.targets])
).replace(
"{excluded_targets}", " ".join(['"%s"' % t for t in ctx.attr.excluded_targets])
)

# Write the script to an output file
script_file = ctx.actions.declare_file("run_scalafix.sh")
Expand All @@ -100,9 +114,9 @@ scalafix_runner = rule(
implementation=_scalafix_runner_impl,
attrs={
"toolchain": attr.label(mandatory=True),
"opts": attr.string(default=":--verbose:--config .scalafix.conf"),
"targets": attr.label_list(mandatory=True, allow_files=False),
"excluded_targets": attr.label_list(default=[], allow_files=False),
"opts": attr.string(default="--verbose --config .scalafix.conf"),
"targets": attr.string_list(mandatory=True), # String list for patterns
"excluded_targets": attr.string_list(default=[]),
},
executable=True,
)

0 comments on commit 38d5e78

Please sign in to comment.