From 4de48e4e4fedc379fb964b0661e2dbe06bbd2d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gergely=20F=C3=A1bi=C3=A1n?= Date: Thu, 3 Jul 2025 15:42:43 +0200 Subject: [PATCH] scala_inlined_library Add a new rule to enable compiling scala code, that is not for macro definitions, but would be used in inlining (so ijar support should be disabled for it). You may need strict dependency checks (transitive dependencies not on the classpath) for code that is to be used in inlining. Till now one would need to use scala_macro_library for that, but it was recently switched to include all transitive dependencies on the compiler's classpath, hence strict dependencies are not enforced there. The new rule enables having both: a) code compiled for inlining b) strict dependency checks --- README.md | 1 + docs/customizable_phase.md | 1 + docs/scala_inline_library.md | 177 +++++++++++++++++++++++++ scala/advanced_usage/scala.bzl | 2 + scala/private/phases/phase_compile.bzl | 12 ++ scala/private/phases/phases.bzl | 2 + scala/private/rules/scala_library.bzl | 63 +++++++++ scala/scala.bzl | 2 + test/BUILD | 22 +++ 9 files changed, 282 insertions(+) create mode 100644 docs/scala_inline_library.md diff --git a/README.md b/README.md index 8c10303ae..e35ca409c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ This project defines core build rules for [Scala](https://www.scala-lang.org/) t - [scala_library](./docs/scala_library.md) - [scala_macro_library](./docs/scala_macro_library.md) +- [scala_inlined_library](./docs/scala_inlined_library.md) - [scala_binary](./docs/scala_binary.md) - [scala_test](./docs/scala_test.md) - [scala_repl](./docs/scala_repl.md) diff --git a/docs/customizable_phase.md b/docs/customizable_phase.md index ee9233f53..17e937935 100644 --- a/docs/customizable_phase.md +++ b/docs/customizable_phase.md @@ -146,6 +146,7 @@ Currently phase architecture is used by 7 rules: - scala_library - scala_macro_library +- scala_inlined_library - scala_library_for_plugin_bootstrapping - scala_binary - scala_test diff --git a/docs/scala_inline_library.md b/docs/scala_inline_library.md new file mode 100644 index 000000000..56267b4ff --- /dev/null +++ b/docs/scala_inline_library.md @@ -0,0 +1,177 @@ +# scala_inlined_library + +```py +scala_inlined_library( + name, + srcs, + deps, + runtime_deps, + exports, + data, + main_class, + resources, + resource_strip_prefix, + scalacopts, + jvm_flags, + scalac_jvm_flags, + javac_jvm_flags, + unused_dependency_checker_mode +) +``` + +`scala_inlined_library` generates a `.jar` file from `.scala` source files. This rule +has the interface jar (`ijar`) functionality disabled. +This is to enable inlining the compiled code, when it is given as a dependency for another Scala target (`ijar` +does not contain an implementation, so it cannot be used for inlining). + +In order to have a Java rule use this jar file, use the `java_import` rule. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Attributes
name +

Name, required

+

A unique name for this target

+
srcs +

List of labels, required

+

List of Scala .scala source files used to build the + library. These may be .srcjar jar files that contain source code.

+
deps +

List of labels, optional

+

List of other libraries to linked to this library target. + These must be jvm targets (scala_library, java_library, java_import, etc...)

+
runtime_deps +

List of labels, optional

+

List of other libraries to put on the classpath only at runtime. This is rarely needed in Scala. + These must be jvm targets (scala_library, java_library, java_import, etc...)

+
exports +

List of labels, optional

+

List of targets to add to the dependencies of those that depend on this target. Similar + to the `java_library` parameter of the same name. Use this sparingly as it weakens the + precision of the build graph. + These must be jvm targets (scala_library, java_library, java_import, etc...)

+
data +

List of labels, optional

+

List of files needed by this rule at runtime.

+
main_class +

String, optional

+

Name of class with main() method to use as an entry point

+

+ The value of this attribute is a class name, not a source file. The + class must be available at runtime: it may be compiled by this rule + (from srcs) or provided by direct or transitive + dependencies (through deps). If the class is unavailable, + the binary will fail at runtime; there is no build-time check. +

+
resources +

List of labels; optional

+

A list of data files to be included in the JAR.

+
resource_strip_prefix +

String; optional

+

+ The path prefix to strip from Java resources. If specified, + this path prefix is stripped from every file in the `resources` attribute. + It is an error for a resource file not to be under this directory. +

+
scalacopts +

List of strings; optional

+

+ Extra compiler options for this library to be passed to scalac. Subject to + Make variable + substitution and + Bourne shell tokenization. +

+
jvm_flags +

List of strings; optional; deprecated

+

+ Deprecated, superseded by scalac_jvm_flags and javac_jvm_flags. Is not used and is kept as backwards compatibility for the near future. Effectively jvm_flags is now an executable target attribute only. +

+
scalac_jvm_flags +

List of strings; optional

+

+ List of JVM flags to be passed to scalac after the + scalacopts. Subject to + Make variable + substitution and + Bourne shell tokenization. +

+
javac_jvm_flags +

List of strings; optional

+

+ List of JVM flags to be passed to javac after the + javacopts. Subject to + Make variable + substitution and + Bourne shell tokenization. +

+
unused_dependency_checker_mode +

String; optional

+

+ Enable unused dependency checking (see Unused dependency checking). + Possible values are: off, warn and error. +

+
diff --git a/scala/advanced_usage/scala.bzl b/scala/advanced_usage/scala.bzl index b632cd0a8..a3a2956bf 100644 --- a/scala/advanced_usage/scala.bzl +++ b/scala/advanced_usage/scala.bzl @@ -13,6 +13,7 @@ load( ) load( "//scala/private:rules/scala_library.bzl", + _make_scala_inlined_library = "make_scala_inlined_library", _make_scala_library = "make_scala_library", _make_scala_library_for_plugin_bootstrapping = "make_scala_library_for_plugin_bootstrapping", _make_scala_macro_library = "make_scala_macro_library", @@ -28,6 +29,7 @@ load( make_scala_binary = _make_scala_binary make_scala_library = _make_scala_library +make_scala_inlined_library = _make_scala_inlined_library make_scala_library_for_plugin_bootstrapping = _make_scala_library_for_plugin_bootstrapping make_scala_macro_library = _make_scala_macro_library make_scala_repl = _make_scala_repl diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl index d923a2663..931e6b401 100644 --- a/scala/private/phases/phase_compile.bzl +++ b/scala/private/phases/phase_compile.bzl @@ -43,6 +43,18 @@ def phase_compile_library(ctx, p): ) return _phase_compile_default(ctx, p, args) +def phase_compile_inlined_library(ctx, p): + args = struct( + buildijar = False, + srcjars = p.collect_srcjars, + unused_dependency_checker_ignored_targets = [ + target.label + for target in p.scalac_provider.default_classpath + ctx.attr.exports + + ctx.attr.unused_dependency_checker_ignored_targets + ], + ) + return _phase_compile_default(ctx, p, args) + def phase_compile_library_for_plugin_bootstrapping(ctx, p): args = struct( buildijar = ctx.attr.build_ijar, diff --git a/scala/private/phases/phases.bzl b/scala/private/phases/phases.bzl index d4f1062dd..3cd92fc50 100644 --- a/scala/private/phases/phases.bzl +++ b/scala/private/phases/phases.bzl @@ -21,6 +21,7 @@ load( "//scala/private:phases/phase_compile.bzl", _phase_compile_binary = "phase_compile_binary", _phase_compile_common = "phase_compile_common", + _phase_compile_inlined_library = "phase_compile_inlined_library", _phase_compile_junit_test = "phase_compile_junit_test", _phase_compile_library = "phase_compile_library", _phase_compile_library_for_plugin_bootstrapping = "phase_compile_library_for_plugin_bootstrapping", @@ -135,6 +136,7 @@ phase_collect_jars_common = _phase_collect_jars_common # compile phase_compile_binary = _phase_compile_binary phase_compile_library = _phase_compile_library +phase_compile_inlined_library = _phase_compile_inlined_library phase_compile_library_for_plugin_bootstrapping = _phase_compile_library_for_plugin_bootstrapping phase_compile_junit_test = _phase_compile_junit_test phase_compile_repl = _phase_compile_repl diff --git a/scala/private/rules/scala_library.bzl b/scala/private/rules/scala_library.bzl index eb2122095..7f17caa6e 100644 --- a/scala/private/rules/scala_library.bzl +++ b/scala/private/rules/scala_library.bzl @@ -24,6 +24,7 @@ load( "phase_collect_jars_common", "phase_collect_jars_macro_library", "phase_collect_srcjars", + "phase_compile_inlined_library", "phase_compile_library", "phase_compile_library_for_plugin_bootstrapping", "phase_coverage_common", @@ -218,6 +219,68 @@ def make_scala_library_for_plugin_bootstrapping(*extras): scala_library_for_plugin_bootstrapping = make_scala_library_for_plugin_bootstrapping() +## +# scala_inlined_library +## + +def _scala_inlined_library_impl(ctx): + # Build up information from dependency-like attributes + return run_phases( + ctx, + # customizable phases + [ + ("scalac_provider", phase_scalac_provider), + ("scalainfo_provider", phase_scalainfo_provider_non_macro), + ("collect_srcjars", phase_collect_srcjars), + ("write_manifest", phase_write_manifest), + ("dependency", phase_dependency_common), + ("collect_jars", phase_collect_jars_common), + ("scalacopts", phase_scalacopts), + ("semanticdb", phase_semanticdb), + ("compile", phase_compile_inlined_library), + ("coverage", phase_coverage_library), + ("merge_jars", phase_merge_jars), + ("runfiles", phase_runfiles_library), + ("collect_exports_jars", phase_collect_exports_jars), + ("default_info", phase_default_info), + ], + ) + +_scala_inlined_library_attrs = {} + +_scala_inlined_library_attrs.update(implicit_deps) + +_scala_inlined_library_attrs.update(common_attrs) + +_scala_inlined_library_attrs.update(_library_attrs) + +_scala_inlined_library_attrs.update(resolve_deps) + +_scala_inlined_library_attrs.update(toolchain_transition_attr) + +def make_scala_inlined_library(*extras): + return rule( + attrs = _dicts.add( + _scala_inlined_library_attrs, + extras_phases(extras), + *[extra["attrs"] for extra in extras if "attrs" in extra] + ), + fragments = ["java"], + outputs = _dicts.add( + common_outputs, + *[extra["outputs"] for extra in extras if "outputs" in extra] + ), + toolchains = [ + "//scala:toolchain_type", + "@bazel_tools//tools/jdk:toolchain_type", + ], + cfg = scala_version_transition, + provides = [JavaInfo], + implementation = _scala_inlined_library_impl, + ) + +scala_inlined_library = make_scala_inlined_library() + ## # scala_macro_library ## diff --git a/scala/scala.bzl b/scala/scala.bzl index 162ff3b60..2f4341b58 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -18,6 +18,7 @@ load( ) load( "//scala/private:rules/scala_library.bzl", + _scala_inlined_library = "scala_inlined_library", _scala_library = "scala_library", _scala_library_for_plugin_bootstrapping = "scala_library_for_plugin_bootstrapping", _scala_library_suite = "scala_library_suite", @@ -63,6 +64,7 @@ scala_library = _scala_library scala_library_for_plugin_bootstrapping = _scala_library_for_plugin_bootstrapping scala_library_suite = _scala_library_suite scala_macro_library = _scala_macro_library +scala_inlined_library = _scala_inlined_library scala_repl = _scala_repl scala_test = _scala_test scala_test_suite = _scala_test_suite diff --git a/test/BUILD b/test/BUILD index 08220d003..d55b5b5e1 100644 --- a/test/BUILD +++ b/test/BUILD @@ -6,6 +6,7 @@ load( "//scala:scala.bzl", "scala_binary", "scala_doc", + "scala_inlined_library", "scala_junit_test", "scala_library", "scala_library_suite", @@ -832,3 +833,24 @@ scala_test( srcs = ["ScalaTestResourcesFromLocalTargetTest.scala"], resources = [":py_resource_binary"], ) + +# scala_inlined_library + +# If this target is a scala_library instead, then InlinedExported will fail to compile. +scala_inlined_library( + name = "InlinableExported", + srcs = ["Exported.scala"], + runtime_deps = ["Runtime"], +) + +scala_library( + name = "InlinedExported", + srcs = ["OtherLib.scala"], + scalacopts = [ + "-opt:l:inline", + "-opt-inline-from:scalarules.test.**", + # We need fatal warnings to ensure that the inlining actually worked. + "-Xfatal-warnings", + ], + deps = [":InlinableExported"], +)