| # Utility rule for IDE integration (Eclipse, IntelliJ, etc.). |
| # |
| # This rule produces metadata files: |
| # |
| # - %{name}.runtime_classpath |
| # One runtime jar path per line. Used to construct the IDE classpath. |
| # |
| # - %{name}.source_classpath |
| # One source-jar path per line. Used to attach sources to libraries in |
| # the IDE. |
| # |
| # - %{name}.processor_classpath |
| # One annotation-processor jar path per line. Used to construct Eclipse .factorypath. |
| # |
| # Important implementation details: |
| # |
| # * With rules_jvm_external, many Maven artifacts (including sources) are |
| # resolved lazily. A jar may appear in a provider but not exist on disk |
| # unless it is consumed by an action. |
| # |
| # * IDEs require real files on disk. Simply listing paths is insufficient. |
| # |
| # * To ensure jars are materialized under the Bazel execution root, this rule |
| # declares them as inputs to small, no-op actions ("stamp" actions). |
| # |
| # * A param file is used for source jars to avoid command-line length limits. |
| # Bazel does not automatically expand "@paramfile" in run_shell, so the |
| # script explicitly strips the '@' prefix and reads the file. |
| |
| load("@rules_java//java:defs.bzl", "JavaInfo") |
| |
| def _classpath_collector_impl(ctx): |
| runtime_sets = [] |
| source_sets = [] |
| processor_sets = [] |
| |
| for d in ctx.attr.deps: |
| if JavaInfo in d: |
| j = d[JavaInfo] |
| |
| runtime_sets.append(j.transitive_runtime_jars) |
| |
| ci = j.compilation_info |
| if ci and hasattr(ci, "runtime_classpath"): |
| runtime_sets.append(ci.runtime_classpath) |
| |
| source_sets.append(j.transitive_source_jars) |
| |
| ap = j.annotation_processing |
| if ap and hasattr(ap, "processor_classpath"): |
| processor_sets.append(ap.processor_classpath) |
| |
| elif hasattr(d, "files"): |
| runtime_sets.append(d.files) |
| |
| runtime_files = depset(transitive = runtime_sets).to_list() |
| source_files = depset(transitive = source_sets).to_list() |
| processor_files = depset(transitive = processor_sets).to_list() |
| |
| # Runtime classpath: metadata file. |
| ctx.actions.write( |
| output = ctx.outputs.runtime, |
| content = "\n".join(sorted([f.path for f in runtime_files])), |
| ) |
| |
| # Force runtime jars to be present on disk. |
| runtime_stamp = ctx.actions.declare_file(ctx.label.name + ".runtime_materialized") |
| ctx.actions.run_shell( |
| inputs = runtime_files, |
| outputs = [runtime_stamp], |
| arguments = [runtime_stamp.path] + [f.path for f in runtime_files], |
| command = r""" |
| set -euo pipefail |
| OUT="$1" |
| shift |
| # Touch/validate inputs to force materialization of symlinks/remote outputs. |
| for f in "$@"; do |
| test -e "$f" |
| done |
| : > "$OUT" |
| """, |
| ) |
| |
| # Source classpath: write stable sorted list, and materialize jars by |
| # declaring them as inputs. |
| pf = ctx.actions.args() |
| pf.set_param_file_format("multiline") |
| pf.use_param_file("%s", use_always = True) |
| pf.add_all([f.path for f in source_files]) |
| |
| ctx.actions.run_shell( |
| inputs = source_files, |
| outputs = [ctx.outputs.sources], |
| arguments = [ctx.outputs.sources.path, pf], |
| command = r""" |
| set -euo pipefail |
| OUT="$1" |
| PF="$2" |
| PF="${PF#@}" |
| if [ -n "$PF" ] && [ -f "$PF" ]; then |
| sort "$PF" > "$OUT" |
| else |
| : > "$OUT" |
| fi |
| """, |
| ) |
| |
| # Processor classpath: metadata file. |
| ctx.actions.write( |
| output = ctx.outputs.processors, |
| content = "\n".join(sorted([f.path for f in processor_files])), |
| ) |
| |
| # Force processor jars to be present on disk. |
| processor_stamp = ctx.actions.declare_file(ctx.label.name + ".processors_materialized") |
| ctx.actions.run_shell( |
| inputs = processor_files, |
| outputs = [processor_stamp], |
| arguments = [processor_stamp.path] + [f.path for f in processor_files], |
| command = r""" |
| set -euo pipefail |
| OUT="$1" |
| shift |
| # Touch/validate inputs to force materialization of symlinks/remote outputs. |
| for f in "$@"; do |
| test -e "$f" |
| done |
| : > "$OUT" |
| """, |
| ) |
| |
| classpath_collector = rule( |
| implementation = _classpath_collector_impl, |
| attrs = { |
| "deps": attr.label_list(), |
| }, |
| outputs = { |
| "runtime": "%{name}.runtime_classpath", |
| "sources": "%{name}.source_classpath", |
| "processors": "%{name}.processor_classpath", |
| }, |
| ) |