diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/WORKSPACE b/WORKSPACE index 708662bce..6e4c1668a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -11,11 +11,19 @@ http_archive( url = "https://github.com/bazelbuild/buildtools/archive/f27d1753c8b3210d9e87cdc9c45bc2739ae2c2db.zip", ) +load("//unstable/multiscala:repositories/rules_jvm_external.bzl", "load_rules_jvm_external") + +load_rules_jvm_external() + load("@com_github_bazelbuild_buildtools//buildifier:deps.bzl", "buildifier_dependencies") buildifier_dependencies() -load("//scala:scala.bzl", "scala_repositories") +load("//scala:toolchains.bzl", "scala_register_toolchains") + +scala_register_toolchains() + +load("//scala:scala_repositories.bzl", "scala_repositories") scala_repositories() diff --git a/private/example/App.scala b/private/example/App.scala new file mode 100644 index 000000000..79148dace --- /dev/null +++ b/private/example/App.scala @@ -0,0 +1,5 @@ +object App extends scala.App { + def version = scala.util.Properties.versionString + + println(s"hello, world from $version!") +} diff --git a/private/example/AppTest.scala b/private/example/AppTest.scala new file mode 100644 index 000000000..a7f9bf841 --- /dev/null +++ b/private/example/AppTest.scala @@ -0,0 +1,9 @@ +import org.scalatest._ + +class AppTest extends FlatSpec with Matchers { + it should "have a successful test" in { + System.err.println(s"hello, world from ${scala.util.Properties.versionString}!") + + App.version should be (scala.util.Properties.versionString) + } +} diff --git a/private/example/BUILD.bazel b/private/example/BUILD.bazel new file mode 100644 index 000000000..4fd5480ca --- /dev/null +++ b/private/example/BUILD.bazel @@ -0,0 +1,26 @@ +load( + "@io_bazel_rules_scala//scala:scala.bzl", + "scala_binary", + "scala_library", + "scala_test", +) + +scala_library( + name = "library", + srcs = glob( + ["*.scala"], + exclude = ["*Test.scala"], + ), +) + +scala_binary( + name = "app", + main_class = "App", + runtime_deps = [":library"], +) + +scala_test( + name = "test", + srcs = ["AppTest.scala"], + deps = [":library"], +) diff --git a/private/example/WORKSPACE b/private/example/WORKSPACE new file mode 100644 index 000000000..3c0f4b098 --- /dev/null +++ b/private/example/WORKSPACE @@ -0,0 +1,27 @@ +load("//repositories:bazel_skylib.bzl", "load_bazel_skylib") + +load_bazel_skylib() + +load("//repositories:rules_jvm_external.bzl", "load_rules_jvm_external") + +load_rules_jvm_external() + +load("//repositories:rules_scala.bzl", "load_rules_scala") + +load_rules_scala() + +load("//repositories:rules_proto.bzl", "load_rules_proto") + +load_rules_proto() + +load("//repositories:rules_python.bzl", "load_rules_python") + +load_rules_python() + +load("@io_bazel_rules_scala//scala:toolchains.bzl", "scala_register_toolchains") + +scala_register_toolchains() + +load("@io_bazel_rules_scala//scala:scala_repositories.bzl", "scala_repositories") + +scala_repositories() diff --git a/private/example/repositories/BUILD.bazel b/private/example/repositories/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/private/example/repositories/bazel_skylib.bzl b/private/example/repositories/bazel_skylib.bzl new file mode 100644 index 000000000..e6477bed7 --- /dev/null +++ b/private/example/repositories/bazel_skylib.bzl @@ -0,0 +1,11 @@ +"""load skylib""" + +load(":tools.bzl", _github_release = "github_release") + +def load_bazel_skylib(): + _github_release( + name = "bazel_skylib", + sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + repository = "bazelbuild/bazel-skylib", + release = "1.0.2", + ) diff --git a/private/example/repositories/rules_jvm_external.bzl b/private/example/repositories/rules_jvm_external.bzl new file mode 100644 index 000000000..d68a0e2c4 --- /dev/null +++ b/private/example/repositories/rules_jvm_external.bzl @@ -0,0 +1,11 @@ +"""load rules_jvm_external""" + +load(":tools.bzl", _github_archive = "github_archive") + +def load_rules_jvm_external(): + _github_archive( + name = "rules_jvm_external", + repository = "bazelbuild/rules_jvm_external", + sha256 = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a", + tag = "3.0", + ) diff --git a/private/example/repositories/rules_proto.bzl b/private/example/repositories/rules_proto.bzl new file mode 100644 index 000000000..bddf5cc67 --- /dev/null +++ b/private/example/repositories/rules_proto.bzl @@ -0,0 +1,11 @@ +"""load rules_proto: needed by protobuf repo""" + +load(":tools.bzl", _github_archive = "github_archive") + +def load_rules_proto(): + _github_archive( + name = "rules_proto", + repository = "bazelbuild/rules_proto", + sha256 = "62847ac7740865d73a2c8199be292bba913d62e79084442f3e829c3058a25e64", + tag = "d7666ec475c1f8d4a6803cbc0a0b6b4374360868", + ) diff --git a/private/example/repositories/rules_python.bzl b/private/example/repositories/rules_python.bzl new file mode 100644 index 000000000..9ab16594c --- /dev/null +++ b/private/example/repositories/rules_python.bzl @@ -0,0 +1,11 @@ +"""load rules_python: needed by protobuf repo""" + +load(":tools.bzl", _github_archive = "github_archive") + +def load_rules_python(): + _github_archive( + name = "rules_python", + repository = "bazelbuild/rules_python", + sha256 = "7d64815f4b22400bed0f1b9da663037e1578573446b7bc78f20f24b2b5459bb9", + tag = "38f86fb55b698c51e8510c807489c9f4e047480e", + ) diff --git a/private/example/repositories/rules_scala.bzl b/private/example/repositories/rules_scala.bzl new file mode 100644 index 000000000..3eb26a745 --- /dev/null +++ b/private/example/repositories/rules_scala.bzl @@ -0,0 +1,7 @@ +"""link back to parent rules_scala repo""" + +def load_rules_scala(): + native.local_repository( + name = "io_bazel_rules_scala", + path = "../..", + ) diff --git a/private/example/repositories/tools.bzl b/private/example/repositories/tools.bzl new file mode 100644 index 000000000..88f628714 --- /dev/null +++ b/private/example/repositories/tools.bzl @@ -0,0 +1,28 @@ +"""helpers for to remove http_archive boilerplate + +N.B.: doesn't currently include the bazel mirros +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def github_release(name, repository, release, sha256): + (org, repo) = repository.split("/") + http_archive( + name = name, + sha256 = sha256, + urls = [ + "https://github.com/{repository}/releases/download/{release}/{repo}-{release}.tar.gz".format(repository = repository, repo = repo, release = release), + ], + ) + +def github_archive(name, repository, sha256, tag): + (org, repo) = repository.split("/") + without_v = tag[1:] if tag.startswith("v") else tag + http_archive( + name = name, + sha256 = sha256, + strip_prefix = "{repo}-{without_v}".format(repo = repo, without_v = without_v), + urls = [ + "https://github.com/{repository}/archive/{tag}.zip".format(repository = repository, tag = tag), + ], + ) diff --git a/scala/BUILD b/scala/BUILD index a5c8f2416..efae634cc 100644 --- a/scala/BUILD +++ b/scala/BUILD @@ -1,8 +1,42 @@ -load( - "@io_bazel_rules_scala//scala:providers.bzl", - _declare_scalac_provider = "declare_scalac_provider", -) +load("//scala:bootstrap_toolchain.bzl", "bootstrap_toolchain") load("//scala:scala_toolchain.bzl", "scala_toolchain") +load("//scala:scala_test_toolchain.bzl", "scala_test_toolchain") + +# default toolchains registered by `scala_register_toolchains` + +# bootstrap toolchain + +toolchain_type( + name = "bootstrap_toolchain_type", + visibility = ["//visibility:public"], +) + +bootstrap_toolchain( + name = "bootstrap_toolchain_impl", + classpath = [ + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_reflect", + ], + macro_classpath = [ + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_reflect", + ], + repl_classpath = [ + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_reflect", + "@io_bazel_rules_scala_scala_compiler", + ], + visibility = ["//visibility:public"], +) + +toolchain( + name = "bootstrap_toolchain", + toolchain = ":bootstrap_toolchain_impl", + toolchain_type = "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + visibility = ["//visibility:public"], +) + +# standard toolchain toolchain_type( name = "toolchain_type", @@ -10,20 +44,48 @@ toolchain_type( ) scala_toolchain( - name = "default_toolchain_impl", + name = "toolchain_impl", + scalac = "//src/java/io/bazel/rulesscala/scalac", scalacopts = [], visibility = ["//visibility:public"], ) toolchain( - name = "default_toolchain", - toolchain = ":default_toolchain_impl", + name = "toolchain", + toolchain = ":toolchain_impl", toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type", visibility = ["//visibility:public"], ) +# test toolchain + +toolchain_type( + name = "scala_test_toolchain_type", + visibility = ["//visibility:public"], +) + +scala_test_toolchain( + name = "scala_test_toolchain_impl", + reporter = "//scala/support:test_reporter", + runner = "//src/java/io/bazel/rulesscala/scala_test:runner", + visibility = ["//visibility:public"], + deps = [ + "//external:io_bazel_rules_scala/dependency/scalatest/scalatest", + ], +) + +toolchain( + name = "scala_test_toolchain", + toolchain = ":scala_test_toolchain_impl", + toolchain_type = "@io_bazel_rules_scala//scala:scala_test_toolchain_type", + visibility = ["//visibility:public"], +) + +# unused dependency checker toolchain + scala_toolchain( name = "unused_dependency_checker_error_toolchain_impl", + scalac = "//src/java/io/bazel/rulesscala/scalac", unused_dependency_checker_mode = "error", visibility = ["//visibility:public"], ) @@ -41,24 +103,6 @@ java_import( visibility = ["//visibility:public"], ) -_declare_scalac_provider( - name = "scalac_default", - default_classpath = [ - "@io_bazel_rules_scala_scala_library", - "@io_bazel_rules_scala_scala_reflect", - ], - default_macro_classpath = [ - "@io_bazel_rules_scala_scala_library", - "@io_bazel_rules_scala_scala_reflect", - ], - default_repl_classpath = [ - "@io_bazel_rules_scala_scala_library", - "@io_bazel_rules_scala_scala_reflect", - "@io_bazel_rules_scala_scala_compiler", - ], - visibility = ["//visibility:public"], -) - java_library( name = "PlaceHolderClassToCreateEmptyJarForScalaImport", srcs = ["PlaceHolderClassToCreateEmptyJarForScalaImport.java"], diff --git a/scala/bootstrap_toolchain.bzl b/scala/bootstrap_toolchain.bzl new file mode 100644 index 000000000..266a4c456 --- /dev/null +++ b/scala/bootstrap_toolchain.bzl @@ -0,0 +1,27 @@ +BootstrapInfo = provider( + doc = "BootstrapInfo", + fields = [ + "classpath", + "macro_classpath", + "repl_classpath", + ], +) + +def _impl(ctx): + toolchain = platform_common.ToolchainInfo( + bootstrapinfo = BootstrapInfo( + classpath = ctx.attr.classpath, + macro_classpath = ctx.attr.macro_classpath, + repl_classpath = ctx.attr.repl_classpath, + ), + ) + return [toolchain, platform_common.TemplateVariableInfo({})] + +bootstrap_toolchain = rule( + _impl, + attrs = { + "classpath": attr.label_list(mandatory = True, allow_files = True), + "repl_classpath": attr.label_list(mandatory = True, allow_files = True), + "macro_classpath": attr.label_list(mandatory = True, allow_files = True), + }, +) diff --git a/scala/plusone.bzl b/scala/plusone.bzl index 1efd17093..fc858c894 100644 --- a/scala/plusone.bzl +++ b/scala/plusone.bzl @@ -10,7 +10,7 @@ PlusOneDeps = provider( ) def _collect_plus_one_deps_aspect_impl(target, ctx): - if (ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode == "off"): + if (ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalainfo.plus_one_deps_mode == "off"): return [] export_plus_one_deps = [] for exported_dep in getattr(ctx.rule.attr, "exports", []): diff --git a/scala/private/common_attributes.bzl b/scala/private/common_attributes.bzl index bd6c3fc3c..09c4c3464 100644 --- a/scala/private/common_attributes.bzl +++ b/scala/private/common_attributes.bzl @@ -98,11 +98,6 @@ implicit_deps = { "_java_runtime": attr.label( default = Label("@bazel_tools//tools/jdk:current_java_runtime"), ), - "_scalac": attr.label( - default = Label( - "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/scalac", - ), - ), "_exe": attr.label( executable = True, cfg = "host", diff --git a/scala/private/coverage_replacements_provider.bzl b/scala/private/coverage_replacements_provider.bzl index 3d45bf64b..da9cc4a13 100644 --- a/scala/private/coverage_replacements_provider.bzl +++ b/scala/private/coverage_replacements_provider.bzl @@ -74,7 +74,7 @@ def _is_enabled(ctx): if "@io_bazel_rules_scala//scala:toolchain_type" not in ctx.toolchains: return False else: - return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].enable_code_coverage_aspect == "on" + return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalainfo.enable_code_coverage_aspect == "on" coverage_replacements_provider = struct( aspect = _aspect, diff --git a/scala/private/macros/scala_repositories.bzl b/scala/private/macros/scala_repositories.bzl index a822993a3..6f3d2e509 100644 --- a/scala/private/macros/scala_repositories.bzl +++ b/scala/private/macros/scala_repositories.bzl @@ -11,6 +11,13 @@ load( "@io_bazel_rules_scala//scala:scala_maven_import_external.bzl", _scala_maven_import_external = "scala_maven_import_external", ) +load( + "@io_bazel_rules_scala//scala/private:rules/scala_library.bzl", + _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", +) def _default_scala_extra_jars(): return { @@ -59,6 +66,8 @@ def scala_repositories( ), maven_servers = _default_maven_server_urls(), scala_extra_jars = _default_scala_extra_jars()): + _disable_multiscala() + (scala_version, scala_version_jar_shas) = scala_version_shas major_version = _extract_major_version(scala_version) @@ -199,3 +208,43 @@ def scala_repositories( name = "io_bazel_rules_scala/dependency/scala/guava", actual = "@io_bazel_rules_scala_guava", ) + + native.bind( + name = "io_bazel_rules_scala/dependency/scalac_rules_commons_io", + actual = "@scalac_rules_commons_io", + ) + + native.bind( + name = "io_bazel_rules_scala/dependency/scala/scalatest/scalatest", + actual = "@io_bazel_rules_scala_scalatest", + ) + + native.bind( + name = "io_bazel_rules_scala/dependency/scala/scalactic/scalactic", + actual = "@io_bazel_rules_scala_scalactic", + ) + +def _repo_impl(ctx): + ctx.file( + "BUILD.bazel", + content = 'exports_files(["configuration.bzl"])', + executable = False, + ) + ctx.file( + "configuration.bzl", + """ +def multiscala_enabled(**kwargs): return False +def configuration(**kwargs): fail("multiscala disabled") +def versions(**kwargs): fail("multiscala disabled") +def versioned_name(**kwargs): fail("multiscala disabled") +""", + ) + +_repo = repository_rule( + implementation = _repo_impl, +) + +def _disable_multiscala(): + _repo( + name = "io_bazel_rules_scala_configuration", + ) diff --git a/scala/private/phases/phase_collect_jars.bzl b/scala/private/phases/phase_collect_jars.bzl index d3d4fce9b..02e1035ff 100644 --- a/scala/private/phases/phase_collect_jars.bzl +++ b/scala/private/phases/phase_collect_jars.bzl @@ -14,24 +14,26 @@ load( ) def phase_collect_jars_scalatest(ctx, p): + boostrap_classpath = ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath + test_classpath = ctx.toolchains["@io_bazel_rules_scala//scala:scala_test_toolchain_type"].scalatestinfo.deps args = struct( - base_classpath = p.scalac_provider.default_classpath + [ctx.attr._scalatest], + base_classpath = boostrap_classpath + test_classpath, extra_runtime_deps = [ - ctx.attr._scalatest_reporter, - ctx.attr._scalatest_runner, + ctx.toolchains["@io_bazel_rules_scala//scala:scala_test_toolchain_type"].scalatestinfo.reporter, + ctx.toolchains["@io_bazel_rules_scala//scala:scala_test_toolchain_type"].scalatestinfo.runner, ], ) return _phase_collect_jars_default(ctx, p, args) def phase_collect_jars_repl(ctx, p): args = struct( - base_classpath = p.scalac_provider.default_repl_classpath, + base_classpath = ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.repl_classpath, ) return _phase_collect_jars_default(ctx, p, args) def phase_collect_jars_macro_library(ctx, p): args = struct( - base_classpath = p.scalac_provider.default_macro_classpath, + base_classpath = ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.macro_classpath, ) return _phase_collect_jars_default(ctx, p, args) @@ -58,7 +60,7 @@ def phase_collect_jars_common(ctx, p): def _phase_collect_jars_default(ctx, p, _args = struct()): return _phase_collect_jars( ctx, - _args.base_classpath if hasattr(_args, "base_classpath") else p.scalac_provider.default_classpath, + _args.base_classpath if hasattr(_args, "base_classpath") else ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath, _args.extra_deps if hasattr(_args, "extra_deps") else [], _args.extra_runtime_deps if hasattr(_args, "extra_runtime_deps") else [], _args.unused_dependency_checker_mode if hasattr(_args, "unused_dependency_checker_mode") else p.unused_deps_checker, diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl index 1e730a574..f8d8106d4 100644 --- a/scala/private/phases/phase_compile.bzl +++ b/scala/private/phases/phase_compile.bzl @@ -35,7 +35,7 @@ def phase_compile_binary(ctx, p): buildijar = False, unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_classpath + + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath + ctx.attr.unused_dependency_checker_ignored_targets ], ) @@ -46,7 +46,7 @@ def phase_compile_library(ctx, p): srcjars = p.collect_srcjars, unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_classpath + ctx.attr.exports + + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath + ctx.attr.exports + ctx.attr.unused_dependency_checker_ignored_targets ], ) @@ -56,7 +56,7 @@ def phase_compile_library_for_plugin_bootstrapping(ctx, p): args = struct( unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_classpath + ctx.attr.exports + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath + ctx.attr.exports ], unused_dependency_checker_mode = "off", ) @@ -67,7 +67,7 @@ def phase_compile_macro_library(ctx, p): buildijar = False, unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_macro_classpath + ctx.attr.exports + + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.macro_classpath + ctx.attr.exports + ctx.attr.unused_dependency_checker_ignored_targets ], ) @@ -82,7 +82,7 @@ def phase_compile_junit_test(ctx, p): ], unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_classpath + + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath + ctx.attr.unused_dependency_checker_ignored_targets ] + [ ctx.attr._junit.label, @@ -98,7 +98,7 @@ def phase_compile_repl(ctx, p): buildijar = False, unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_repl_classpath + + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.repl_classpath + ctx.attr.unused_dependency_checker_ignored_targets ], ) @@ -109,7 +109,7 @@ def phase_compile_scalatest(ctx, p): buildijar = False, unused_dependency_checker_ignored_targets = [ target.label - for target in p.scalac_provider.default_classpath + + for target in ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath + ctx.attr.unused_dependency_checker_ignored_targets ], ) @@ -144,7 +144,7 @@ def _phase_compile( transitive_compile_jars = p.collect_jars.transitive_compile_jars jars2labels = p.collect_jars.jars2labels.jars_to_labels deps_providers = p.collect_jars.deps_providers - default_classpath = p.scalac_provider.default_classpath + default_classpath = ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.classpath out = _compile_or_empty( ctx, @@ -228,7 +228,6 @@ def _compile_or_empty( ctx.attr.print_compile_time, ctx.attr.expect_java_output, ctx.attr.scalac_jvm_flags, - ctx.attr._scalac, unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets, unused_dependency_checker_mode = unused_dependency_checker_mode, diff --git a/scala/private/phases/phase_scalac_provider.bzl b/scala/private/phases/phase_scalac_provider.bzl deleted file mode 100644 index aff54f32f..000000000 --- a/scala/private/phases/phase_scalac_provider.bzl +++ /dev/null @@ -1,12 +0,0 @@ -# -# PHASE: scalac provider -# -# DOCUMENT THIS -# -load( - "@io_bazel_rules_scala//scala:providers.bzl", - _ScalacProvider = "ScalacProvider", -) - -def phase_scalac_provider(ctx, p): - return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalac_provider_attr[_ScalacProvider] diff --git a/scala/private/phases/phase_unused_deps_checker.bzl b/scala/private/phases/phase_unused_deps_checker.bzl index 21f0daebb..6529e7b35 100644 --- a/scala/private/phases/phase_unused_deps_checker.bzl +++ b/scala/private/phases/phase_unused_deps_checker.bzl @@ -8,4 +8,4 @@ def phase_unused_deps_checker(ctx, p): if ctx.attr.unused_dependency_checker_mode: return ctx.attr.unused_dependency_checker_mode else: - return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].unused_dependency_checker_mode + return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalainfo.unused_dependency_checker_mode diff --git a/scala/private/phases/phase_write_executable.bzl b/scala/private/phases/phase_write_executable.bzl index 3c8c11fbc..e92602243 100644 --- a/scala/private/phases/phase_write_executable.bzl +++ b/scala/private/phases/phase_write_executable.bzl @@ -21,7 +21,7 @@ def phase_write_executable_scalatest(ctx, p): # toolchain final_jvm_flags = first_non_empty( ctx.attr.jvm_flags, - ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_test_jvm_flags, + ctx.toolchains["@io_bazel_rules_scala//scala:scala_test_toolchain_type"].scalatestinfo.scala_test_jvm_flags, ) args = struct( rjars = p.coverage_runfiles.rjars, diff --git a/scala/private/phases/phases.bzl b/scala/private/phases/phases.bzl index cc2396684..b18798da5 100644 --- a/scala/private/phases/phases.bzl +++ b/scala/private/phases/phases.bzl @@ -46,7 +46,6 @@ load( _phase_runfiles_scalatest = "phase_runfiles_scalatest", ) load("@io_bazel_rules_scala//scala/private:phases/phase_default_info.bzl", _phase_default_info = "phase_default_info") -load("@io_bazel_rules_scala//scala/private:phases/phase_scalac_provider.bzl", _phase_scalac_provider = "phase_scalac_provider") load("@io_bazel_rules_scala//scala/private:phases/phase_write_manifest.bzl", _phase_write_manifest = "phase_write_manifest") load("@io_bazel_rules_scala//scala/private:phases/phase_collect_srcjars.bzl", _phase_collect_srcjars = "phase_collect_srcjars") load("@io_bazel_rules_scala//scala/private:phases/phase_collect_exports_jars.bzl", _phase_collect_exports_jars = "phase_collect_exports_jars") @@ -61,9 +60,6 @@ load("@io_bazel_rules_scala//scala/private:phases/phase_scalafmt.bzl", _phase_sc run_phases = _run_phases extras_phases = _extras_phases -# scalac_provider -phase_scalac_provider = _phase_scalac_provider - # collect_srcjars phase_collect_srcjars = _phase_collect_srcjars diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl index 68f16eeba..dd556b082 100644 --- a/scala/private/rule_impls.bzl +++ b/scala/private/rule_impls.bzl @@ -60,7 +60,6 @@ def compile_scala( print_compile_time, expect_java_output, scalac_jvm_flags, - scalac, unused_dependency_checker_mode = "off", unused_dependency_checker_ignored_targets = []): # look for any plugins: @@ -136,7 +135,7 @@ CurrentTarget: {current_target} compiler_classpath = _join_path(compiler_classpath_jars.to_list(), separator) toolchain = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"] - scalacopts = [ctx.expand_location(v, input_plugins) for v in toolchain.scalacopts + in_scalacopts] + scalacopts = [ctx.expand_location(v, input_plugins) for v in toolchain.scalainfo.scalacopts + in_scalacopts] resource_paths = _resource_paths(resources, resource_strip_prefix) scalac_args = """ @@ -187,7 +186,7 @@ StatsfileOutput: {statsfile_output} ) scalac_inputs, _, scalac_input_manifests = ctx.resolve_command( - tools = [scalac], + tools = [ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalainfo.scalac], ) outs = [output, statsfile] @@ -197,17 +196,19 @@ StatsfileOutput: {statsfile_output} resource_jars + [manifest, argfile] + scalac_inputs ) + scalainfo = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalainfo + # scalac_jvm_flags passed in on the target override scalac_jvm_flags passed in on the # toolchain final_scalac_jvm_flags = first_non_empty( scalac_jvm_flags, - ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalac_jvm_flags, + scalainfo.scalac_jvm_flags, ) ctx.actions.run( inputs = ins, outputs = outs, - executable = scalac.files_to_run.executable, + executable = scalainfo.scalac.files_to_run.executable, input_manifests = scalac_input_manifests, mnemonic = "Scalac", progress_message = "scala %s" % target_label, @@ -250,7 +251,7 @@ def is_dependency_analyzer_off(ctx): return not is_dependency_analyzer_on(ctx) def is_plus_one_deps_off(ctx): - return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].plus_one_deps_mode == "off" + return ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalainfo.plus_one_deps_mode == "off" def is_windows(ctx): return ctx.configuration.host_path_separator == ";" diff --git a/scala/private/rules/scala_binary.bzl b/scala/private/rules/scala_binary.bzl index 07afd2574..9b8d3261a 100644 --- a/scala/private/rules/scala_binary.bzl +++ b/scala/private/rules/scala_binary.bzl @@ -19,7 +19,6 @@ load( "phase_java_wrapper_common", "phase_merge_jars", "phase_runfiles_common", - "phase_scalac_provider", "phase_unused_deps_checker", "phase_write_executable_common", "phase_write_manifest", @@ -31,7 +30,6 @@ def _scala_binary_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("write_manifest", phase_write_manifest), ("unused_deps_checker", phase_unused_deps_checker), ("collect_jars", phase_collect_jars_common), @@ -73,7 +71,10 @@ def make_scala_binary(*extras): common_outputs, *[extra["outputs"] for extra in extras if "outputs" in extra] ), - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + ], implementation = _scala_binary_impl, ) diff --git a/scala/private/rules/scala_junit_test.bzl b/scala/private/rules/scala_junit_test.bzl index 8b58180b7..c70fbc39b 100644 --- a/scala/private/rules/scala_junit_test.bzl +++ b/scala/private/rules/scala_junit_test.bzl @@ -19,7 +19,6 @@ load( "phase_jvm_flags", "phase_merge_jars", "phase_runfiles_common", - "phase_scalac_provider", "phase_unused_deps_checker", "phase_write_executable_junit_test", "phase_write_manifest", @@ -35,7 +34,6 @@ def _scala_junit_test_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("write_manifest", phase_write_manifest), ("unused_deps_checker", phase_unused_deps_checker), ("collect_jars", phase_collect_jars_junit_test), @@ -125,7 +123,13 @@ def make_scala_junit_test(*extras): *[extra["outputs"] for extra in extras if "outputs" in extra] ), test = True, - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + # unclear on next and will consider in factoring, whether + # scalatest and junit tests should be different toolchain types + "@io_bazel_rules_scala//scala:scala_test_toolchain_type", + ], implementation = _scala_junit_test_impl, ) diff --git a/scala/private/rules/scala_library.bzl b/scala/private/rules/scala_library.bzl index 17356b843..0ffe81b46 100644 --- a/scala/private/rules/scala_library.bzl +++ b/scala/private/rules/scala_library.bzl @@ -29,7 +29,6 @@ load( "phase_default_info", "phase_merge_jars", "phase_runfiles_library", - "phase_scalac_provider", "phase_unused_deps_checker", "phase_write_manifest", "run_phases", @@ -57,7 +56,6 @@ def _scala_library_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("collect_srcjars", phase_collect_srcjars), ("write_manifest", phase_write_manifest), ("unused_deps_checker", phase_unused_deps_checker), @@ -92,7 +90,10 @@ def make_scala_library(*extras): common_outputs, *[extra["outputs"] for extra in extras if "outputs" in extra] ), - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + ], implementation = _scala_library_impl, ) @@ -134,7 +135,6 @@ def _scala_library_for_plugin_bootstrapping_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("collect_srcjars", phase_collect_srcjars), ("write_manifest", phase_write_manifest), ("collect_jars", phase_collect_jars_library_for_plugin_bootstrapping), @@ -173,7 +173,10 @@ def make_scala_library_for_plugin_bootstrapping(*extras): common_outputs, *[extra["outputs"] for extra in extras if "outputs" in extra] ), - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + ], implementation = _scala_library_for_plugin_bootstrapping_impl, ) @@ -188,7 +191,6 @@ def _scala_macro_library_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("collect_srcjars", phase_collect_srcjars), ("write_manifest", phase_write_manifest), ("unused_deps_checker", phase_unused_deps_checker), @@ -238,7 +240,10 @@ def make_scala_macro_library(*extras): common_outputs, *[extra["outputs"] for extra in extras if "outputs" in extra] ), - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + ], implementation = _scala_macro_library_impl, ) diff --git a/scala/private/rules/scala_repl.bzl b/scala/private/rules/scala_repl.bzl index 3d508af45..3aa3bd770 100644 --- a/scala/private/rules/scala_repl.bzl +++ b/scala/private/rules/scala_repl.bzl @@ -19,7 +19,6 @@ load( "phase_java_wrapper_repl", "phase_merge_jars", "phase_runfiles_common", - "phase_scalac_provider", "phase_unused_deps_checker", "phase_write_executable_repl", "phase_write_manifest", @@ -31,7 +30,6 @@ def _scala_repl_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("write_manifest", phase_write_manifest), ("unused_deps_checker", phase_unused_deps_checker), # need scala-compiler for MainGenericRunner below @@ -72,7 +70,10 @@ def make_scala_repl(*extras): common_outputs, *[extra["outputs"] for extra in extras if "outputs" in extra] ), - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + ], implementation = _scala_repl_impl, ) diff --git a/scala/private/rules/scala_test.bzl b/scala/private/rules/scala_test.bzl index 1a6bd3e7d..a67775704 100644 --- a/scala/private/rules/scala_test.bzl +++ b/scala/private/rules/scala_test.bzl @@ -20,7 +20,6 @@ load( "phase_java_wrapper_common", "phase_merge_jars", "phase_runfiles_scalatest", - "phase_scalac_provider", "phase_unused_deps_checker", "phase_write_executable_scalatest", "phase_write_manifest", @@ -32,7 +31,6 @@ def _scala_test_impl(ctx): ctx, # customizable phases [ - ("scalac_provider", phase_scalac_provider), ("write_manifest", phase_write_manifest), ("unused_deps_checker", phase_unused_deps_checker), ("collect_jars", phase_collect_jars_scalatest), @@ -55,18 +53,6 @@ _scala_test_attrs = { "colors": attr.bool(default = True), "full_stacktraces": attr.bool(default = True), "jvm_flags": attr.string_list(), - "_scalatest": attr.label( - default = Label( - "//external:io_bazel_rules_scala/dependency/scalatest/scalatest", - ), - ), - "_scalatest_runner": attr.label( - cfg = "host", - default = Label("//src/java/io/bazel/rulesscala/scala_test:runner"), - ), - "_scalatest_reporter": attr.label( - default = Label("//scala/support:test_reporter"), - ), "_jacocorunner": attr.label( default = Label("@bazel_tools//tools/jdk:JacocoCoverage"), ), @@ -111,7 +97,11 @@ def make_scala_test(*extras): *[extra["outputs"] for extra in extras if "outputs" in extra] ), test = True, - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + "@io_bazel_rules_scala//scala:toolchain_type", + "@io_bazel_rules_scala//scala:scala_test_toolchain_type", + ], implementation = _scala_test_impl, ) diff --git a/scala/providers.bzl b/scala/providers.bzl deleted file mode 100644 index 66fc97f0b..000000000 --- a/scala/providers.bzl +++ /dev/null @@ -1,26 +0,0 @@ -ScalacProvider = provider( - doc = "ScalacProvider", - fields = [ - "default_classpath", - "default_macro_classpath", - "default_repl_classpath", - ], -) - -def _declare_scalac_provider(ctx): - return [ - ScalacProvider( - default_classpath = ctx.attr.default_classpath, - default_repl_classpath = ctx.attr.default_repl_classpath, - default_macro_classpath = ctx.attr.default_macro_classpath, - ), - ] - -declare_scalac_provider = rule( - implementation = _declare_scalac_provider, - attrs = { - "default_classpath": attr.label_list(allow_files = True), - "default_repl_classpath": attr.label_list(allow_files = True), - "default_macro_classpath": attr.label_list(allow_files = True), - }, -) diff --git a/scala/scala.bzl b/scala/scala.bzl index fb4a9eeaf..53db81486 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -2,13 +2,9 @@ load( "@io_bazel_rules_scala//specs2:specs2_junit.bzl", _specs2_junit_dependencies = "specs2_junit_dependencies", ) -load( - "@io_bazel_rules_scala//scala/private:macros/scala_repositories.bzl", - _scala_repositories = "scala_repositories", -) load( "@io_bazel_rules_scala//scala/private:rules/scala_binary.bzl", - _scala_binary = "scala_binary", + _uniscala_scala_binary = "scala_binary", ) load( "@io_bazel_rules_scala//scala/private:rules/scala_doc.bzl", @@ -20,19 +16,35 @@ load( ) load( "@io_bazel_rules_scala//scala/private:rules/scala_library.bzl", - _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", + _uniscala_scala_library = "scala_library", + _uniscala_scala_library_for_plugin_bootstrapping = "scala_library_for_plugin_bootstrapping", + _uniscala_scala_library_suite = "scala_library_suite", + _uniscala_scala_macro_library = "scala_macro_library", ) load( "@io_bazel_rules_scala//scala/private:rules/scala_repl.bzl", - _scala_repl = "scala_repl", + _uniscala_scala_repl = "scala_repl", ) load( "@io_bazel_rules_scala//scala/private:rules/scala_test.bzl", - _scala_test = "scala_test", - _scala_test_suite = "scala_test_suite", + _uniscala_scala_test = "scala_test", + _uniscala_scala_test_suite = "scala_test_suite", +) +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _multiscala_enabled = "multiscala_enabled", +) +load( + "//unstable/multiscala/private:macros/scala_binary.bzl", + _multiscala_scala_binary = "scala_binary", +) +load( + "//unstable/multiscala/private:macros/scala_library.bzl", + _multiscala_scala_library = "scala_library", +) +load( + "//unstable/multiscala/private:macros/scala_test.bzl", + _multiscala_scala_test = "scala_test", ) def scala_specs2_junit_test(name, **kwargs): @@ -48,15 +60,21 @@ def scala_specs2_junit_test(name, **kwargs): **kwargs ) +def _demux(uniscala, multiscala): + return multiscala if _multiscala_enabled() else uniscala + # Re-export private rules for public consumption -scala_binary = _scala_binary +scala_binary = _demux(_uniscala_scala_binary, _multiscala_scala_binary) +scala_library = _demux(_uniscala_scala_library, _multiscala_scala_library) +scala_test = _demux(_uniscala_scala_test, _multiscala_scala_test) + scala_doc = _scala_doc scala_junit_test = _scala_junit_test -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_repl = _scala_repl -scala_repositories = _scala_repositories -scala_test = _scala_test -scala_test_suite = _scala_test_suite +scala_library_for_plugin_bootstrapping = _uniscala_scala_library_for_plugin_bootstrapping +scala_library_suite = _uniscala_scala_library_suite +scala_macro_library = _uniscala_scala_macro_library +scala_repl = _uniscala_scala_repl +scala_test_suite = _uniscala_scala_test_suite + +def scala_repositories(**kwargs): + fail("please import scala_repositories from @io_bazel_rules_scala//scala:scala_repositories.bzl") diff --git a/scala/scala_repositories.bzl b/scala/scala_repositories.bzl new file mode 100644 index 000000000..de917cad1 --- /dev/null +++ b/scala/scala_repositories.bzl @@ -0,0 +1,6 @@ +load( + "@io_bazel_rules_scala//scala/private:macros/scala_repositories.bzl", + _scala_repositories = "scala_repositories", +) + +scala_repositories = _scala_repositories diff --git a/scala/scala_test_toolchain.bzl b/scala/scala_test_toolchain.bzl new file mode 100644 index 000000000..731e9230e --- /dev/null +++ b/scala/scala_test_toolchain.bzl @@ -0,0 +1,34 @@ +ScalaTestInfo = provider( + doc = "ScalaTestInfo", + fields = [ + "deps", + "reporter", + "runner", + "scala_test_jvm_flags", + ], +) + +def _scala_test_toolchain_impl(ctx): + scalatestinfo = ScalaTestInfo( + deps = ctx.attr.deps, + reporter = ctx.attr.reporter, + runner = ctx.attr.runner, + scala_test_jvm_flags = ctx.attr.scala_test_jvm_flags, + ) + toolchain = platform_common.ToolchainInfo( + scalatestinfo = scalatestinfo, + ) + return [toolchain, platform_common.TemplateVariableInfo({})] + +scala_test_toolchain = rule( + _scala_test_toolchain_impl, + attrs = { + "deps": attr.label_list( + providers = [JavaInfo], + mandatory = True, + ), + "reporter": attr.label(mandatory = True), + "runner": attr.label(mandatory = True), + "scala_test_jvm_flags": attr.string_list(), + }, +) diff --git a/scala/scala_toolchain.bzl b/scala/scala_toolchain.bzl index f57e23302..574582527 100644 --- a/scala/scala_toolchain.bzl +++ b/scala/scala_toolchain.bzl @@ -1,28 +1,33 @@ -load( - "@io_bazel_rules_scala//scala:providers.bzl", - _ScalacProvider = "ScalacProvider", +ScalaInfo = provider( + doc = "ScalaInfo", + fields = [ + "scalacopts", + "unused_dependency_checker_mode", + "plus_one_deps_mode", + "enable_code_coverage_aspect", + "scalac_jvm_flags", + "scalac", + ], ) def _scala_toolchain_impl(ctx): - toolchain = platform_common.ToolchainInfo( + scalainfo = ScalaInfo( scalacopts = ctx.attr.scalacopts, - scalac_provider_attr = ctx.attr.scalac_provider_attr, unused_dependency_checker_mode = ctx.attr.unused_dependency_checker_mode, plus_one_deps_mode = ctx.attr.plus_one_deps_mode, enable_code_coverage_aspect = ctx.attr.enable_code_coverage_aspect, scalac_jvm_flags = ctx.attr.scalac_jvm_flags, - scala_test_jvm_flags = ctx.attr.scala_test_jvm_flags, + scalac = ctx.attr.scalac, + ) + toolchain = platform_common.ToolchainInfo( + scalainfo = scalainfo, ) - return [toolchain] + return [toolchain, platform_common.TemplateVariableInfo({})] scala_toolchain = rule( _scala_toolchain_impl, attrs = { "scalacopts": attr.string_list(), - "scalac_provider_attr": attr.label( - default = "@io_bazel_rules_scala//scala:scalac_default", - providers = [_ScalacProvider], - ), "unused_dependency_checker_mode": attr.string( default = "off", values = ["off", "warn", "error"], @@ -36,6 +41,6 @@ scala_toolchain = rule( values = ["off", "on"], ), "scalac_jvm_flags": attr.string_list(), - "scala_test_jvm_flags": attr.string_list(), + "scalac": attr.label(mandatory = True, allow_files = True), }, ) diff --git a/scala/scalatest/BUILD b/scala/scalatest/BUILD index 2e4ad67bb..4e7439e96 100644 --- a/scala/scalatest/BUILD +++ b/scala/scalatest/BUILD @@ -6,7 +6,7 @@ scala_import( name = "scalatest", jars = [], exports = [ - "@io_bazel_rules_scala_scalactic", - "@io_bazel_rules_scala_scalatest", + "//external:io_bazel_rules_scala/dependency/scala/scalactic/scalactic", + "//external:io_bazel_rules_scala/dependency/scala/scalatest/scalatest", ], ) diff --git a/scala/support/multiscala.bzl b/scala/support/multiscala.bzl new file mode 100644 index 000000000..022c78f73 --- /dev/null +++ b/scala/support/multiscala.bzl @@ -0,0 +1,4 @@ +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + "scala_library", +) diff --git a/scala/toolchains.bzl b/scala/toolchains.bzl index 89a2bdc08..c4f438f50 100644 --- a/scala/toolchains.bzl +++ b/scala/toolchains.bzl @@ -1,5 +1,7 @@ def scala_register_toolchains(): - native.register_toolchains("@io_bazel_rules_scala//scala:default_toolchain") + native.register_toolchains("@io_bazel_rules_scala//scala:bootstrap_toolchain") + native.register_toolchains("@io_bazel_rules_scala//scala:toolchain") + native.register_toolchains("@io_bazel_rules_scala//scala:scala_test_toolchain") def scala_register_unused_deps_toolchains(): native.register_toolchains( diff --git a/scala_proto/default_dep_sets.bzl b/scala_proto/default_dep_sets.bzl index 345890cd8..6a5878727 100644 --- a/scala_proto/default_dep_sets.bzl +++ b/scala_proto/default_dep_sets.bzl @@ -11,7 +11,6 @@ DEFAULT_SCALAPB_COMPILE_DEPS = [ "//external:io_bazel_rules_scala/dependency/com_google_protobuf/protobuf_java", "//external:io_bazel_rules_scala/dependency/proto/scalapb_lenses", "//external:io_bazel_rules_scala/dependency/proto/scalapb_fastparse", - "//external:io_bazel_rules_scala/dependency/scala/scala_library", ] DEFAULT_SCALAPB_GRPC_DEPS = [ diff --git a/scala_proto/private/scalapb_aspect.bzl b/scala_proto/private/scalapb_aspect.bzl index 0d51cfb65..47c7605f2 100644 --- a/scala_proto/private/scalapb_aspect.bzl +++ b/scala_proto/private/scalapb_aspect.bzl @@ -46,7 +46,6 @@ def _compiled_jar_file(actions, scalapb_jar): def _compile_scala( ctx, - scalac, label, output, scalapb_jar, @@ -88,7 +87,6 @@ def _compile_scala( print_compile_time = False, expect_java_output = False, scalac_jvm_flags = [], - scalac = scalac, ) return JavaInfo( @@ -134,8 +132,9 @@ def _scalapb_aspect_impl(target, ctx): transitive_protos = sorted(target_ti.transitive_sources.to_list()) toolchain = ctx.toolchains["@io_bazel_rules_scala//scala_proto:toolchain_type"] + boostrap_toolchain = ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"] flags = [] - imps = [j[JavaInfo] for j in ctx.attr._implicit_compile_deps] + imps = [j[JavaInfo] for j in ctx.attr._implicit_compile_deps + boostrap_toolchain.bootstrapinfo.classpath] if toolchain.with_grpc: flags.append("grpc") @@ -189,7 +188,6 @@ def _scalapb_aspect_impl(target, ctx): outs = depset([output]) java_info = _compile_scala( ctx, - toolchain.scalac, target.label, output, scalapb_file, @@ -236,6 +234,7 @@ scalapb_aspect = aspect( ]), }, toolchains = [ + "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", "@io_bazel_rules_scala//scala:toolchain_type", "@io_bazel_rules_scala//scala_proto:toolchain_type", ], diff --git a/scala_proto/scala_proto_toolchain.bzl b/scala_proto/scala_proto_toolchain.bzl index a733a2fbf..1ef1ad98e 100644 --- a/scala_proto/scala_proto_toolchain.bzl +++ b/scala_proto/scala_proto_toolchain.bzl @@ -1,5 +1,3 @@ -load("//scala_proto:default_dep_sets.bzl", "DEFAULT_SCALAPB_COMPILE_DEPS", "DEFAULT_SCALAPB_GRPC_DEPS") - def _scala_proto_toolchain_impl(ctx): toolchain = platform_common.ToolchainInfo( with_grpc = ctx.attr.with_grpc, @@ -8,7 +6,6 @@ def _scala_proto_toolchain_impl(ctx): blacklisted_protos = ctx.attr.blacklisted_protos, code_generator = ctx.attr.code_generator, extra_generator_dependencies = ctx.attr.extra_generator_dependencies, - scalac = ctx.attr.scalac, named_generators = ctx.attr.named_generators, ) return [toolchain] @@ -36,10 +33,5 @@ scala_proto_toolchain = rule( "extra_generator_dependencies": attr.label_list( providers = [JavaInfo], ), - "scalac": attr.label( - default = Label( - "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/scalac", - ), - ), }, ) diff --git a/src/java/io/bazel/rulesscala/scala_test/BUILD b/src/java/io/bazel/rulesscala/scala_test/BUILD index 2b4f1d851..053e0487a 100644 --- a/src/java/io/bazel/rulesscala/scala_test/BUILD +++ b/src/java/io/bazel/rulesscala/scala_test/BUILD @@ -1,3 +1,7 @@ +load(":multiscala.bzl", "load_multiscala") + +load_multiscala() + java_library( name = "runner", srcs = ["Runner.java"], diff --git a/src/java/io/bazel/rulesscala/scala_test/multiscala.bzl b/src/java/io/bazel/rulesscala/scala_test/multiscala.bzl new file mode 100644 index 000000000..fcf05682d --- /dev/null +++ b/src/java/io/bazel/rulesscala/scala_test/multiscala.bzl @@ -0,0 +1,22 @@ +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _configuration = "configuration", + _multiscala_enabled = "multiscala_enabled", + _versioned_name = "versioned_name", + _versions = "versions", +) + +def load_multiscala(): + if not _multiscala_enabled(): + return + + for version_configuration in _versions(): + native.java_library( + name = _versioned_name("runner", version_configuration), + srcs = ["Runner.java"], + visibility = ["//visibility:public"], + deps = [ + "//external:io_bazel_rules_scala/dependency/scalatest/scalatest", + "@bazel_tools//tools/java/runfiles", + ], + ) diff --git a/src/java/io/bazel/rulesscala/scalac/BUILD b/src/java/io/bazel/rulesscala/scalac/BUILD index ec7f89795..f42821ef7 100644 --- a/src/java/io/bazel/rulesscala/scalac/BUILD +++ b/src/java/io/bazel/rulesscala/scalac/BUILD @@ -1,9 +1,12 @@ load( ":jvm_export_toolchain.bzl", - _export_scalac_repositories_from_toolchain_to_jvm = "export_scalac_repositories_from_toolchain_to_jvm", + "export_scalac_repositories_from_toolchain_to_jvm", ) +load(":multiscala.bzl", "load_multiscala") -_export_scalac_repositories_from_toolchain_to_jvm( +load_multiscala() + +export_scalac_repositories_from_toolchain_to_jvm( name = "exported_scalac_repositories_from_toolchain_to_jvm", ) @@ -20,10 +23,10 @@ java_binary( visibility = ["//visibility:public"], deps = [ ":exported_scalac_repositories_from_toolchain_to_jvm", + "//external:io_bazel_rules_scala/dependency/scalac_rules_commons_io", "//third_party/bazel/src/main/protobuf:worker_protocol_java_proto", "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/jar", "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/worker", - "@scalac_rules_commons_io//jar", ], ) diff --git a/src/java/io/bazel/rulesscala/scalac/jvm_export_toolchain.bzl b/src/java/io/bazel/rulesscala/scalac/jvm_export_toolchain.bzl index 6636a3add..170624462 100644 --- a/src/java/io/bazel/rulesscala/scalac/jvm_export_toolchain.bzl +++ b/src/java/io/bazel/rulesscala/scalac/jvm_export_toolchain.bzl @@ -1,8 +1,3 @@ -load( - "@io_bazel_rules_scala//scala:providers.bzl", - _ScalacProvider = "ScalacProvider", -) - def _files_of(deps): files = [] for dep in deps: @@ -10,14 +5,20 @@ def _files_of(deps): return depset(transitive = files) def _export_scalac_repositories_from_toolchain_to_jvm_impl(ctx): - default_repl_classpath_deps = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scalac_provider_attr[_ScalacProvider].default_repl_classpath + default_repl_classpath_deps = ctx.toolchains["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"].bootstrapinfo.repl_classpath default_repl_classpath_files = _files_of( default_repl_classpath_deps, ).to_list() providers = [JavaInfo(output_jar = jar, compile_jar = jar) for jar in default_repl_classpath_files] return [java_common.merge(providers)] -export_scalac_repositories_from_toolchain_to_jvm = rule( +_export_scalac_repositories_from_toolchain_to_jvm_rule = rule( _export_scalac_repositories_from_toolchain_to_jvm_impl, - toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + toolchains = ["@io_bazel_rules_scala//scala:bootstrap_toolchain_type"], ) + +def export_scalac_repositories_from_toolchain_to_jvm(name, toolchains = None): + kwargs = {} + if (toolchains): + kwargs["toolchains"] = toolchains + _export_scalac_repositories_from_toolchain_to_jvm_rule(name = name, **kwargs) diff --git a/src/java/io/bazel/rulesscala/scalac/multiscala.bzl b/src/java/io/bazel/rulesscala/scalac/multiscala.bzl new file mode 100644 index 000000000..aad3a42e7 --- /dev/null +++ b/src/java/io/bazel/rulesscala/scalac/multiscala.bzl @@ -0,0 +1,46 @@ +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _configuration = "configuration", + _multiscala_enabled = "multiscala_enabled", + _versioned_name = "versioned_name", + _versions = "versions", +) +load( + ":jvm_export_toolchain.bzl", + _export_scalac_repositories_from_toolchain_to_jvm = "export_scalac_repositories_from_toolchain_to_jvm", +) + +def load_multiscala(): + if not _multiscala_enabled(): + return + + for version_configuration in _versions(): + exported_name = _versioned_name( + "exported_scalac_repositories_from_toolchain_to_jvm", + version_configuration, + ) + + _export_scalac_repositories_from_toolchain_to_jvm( + name = exported_name, + toolchains = [version_configuration["bootstrap_toolchain"]], + ) + + native.java_binary( + name = _versioned_name("scalac", version_configuration), + srcs = [ + "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/scalac:scalac_files", + ], + javacopts = [ + "-source 1.8", + "-target 1.8", + ], + main_class = "io.bazel.rulesscala.scalac.ScalaCInvoker", + visibility = ["//visibility:public"], + deps = [ + ":" + exported_name, + "//external:io_bazel_rules_scala/dependency/scalac_rules_commons_io", + "//third_party/bazel/src/main/protobuf:worker_protocol_java_proto", + "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/jar", + "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/worker", + ], + ) diff --git a/test/coverage/BUILD b/test/coverage/BUILD index 1d3cc583b..0adeeb6f6 100644 --- a/test/coverage/BUILD +++ b/test/coverage/BUILD @@ -4,6 +4,7 @@ load("//scala:scala_toolchain.bzl", "scala_toolchain") scala_toolchain( name = "enable_code_coverage_aspect_impl", enable_code_coverage_aspect = "on", + scalac = "//src/java/io/bazel/rulesscala/scalac", visibility = ["//visibility:public"], ) diff --git a/test/src/main/scala/scalarules/test/twitter_scrooge/twitter_scrooge_test.bzl b/test/src/main/scala/scalarules/test/twitter_scrooge/twitter_scrooge_test.bzl index 8432659fc..1805d642c 100644 --- a/test/src/main/scala/scalarules/test/twitter_scrooge/twitter_scrooge_test.bzl +++ b/test/src/main/scala/scalarules/test/twitter_scrooge/twitter_scrooge_test.bzl @@ -1,4 +1,4 @@ -load("@bazel_skylib//:lib.bzl", "asserts", "unittest") +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") load("//twitter_scrooge:twitter_scrooge.bzl", "scrooge_scala_library") load("//thrift:thrift.bzl", "thrift_library") diff --git a/third_party/dependency_analyzer/src/test/BUILD b/third_party/dependency_analyzer/src/test/BUILD index 659c7ed06..def70457f 100644 --- a/third_party/dependency_analyzer/src/test/BUILD +++ b/third_party/dependency_analyzer/src/test/BUILD @@ -37,9 +37,6 @@ scala_test( ], unused_dependency_checker_mode = "off", deps = [ - "//external:io_bazel_rules_scala/dependency/scala/scala_compiler", - "//external:io_bazel_rules_scala/dependency/scala/scala_library", - "//external:io_bazel_rules_scala/dependency/scala/scala_reflect", "//third_party/dependency_analyzer/src/main:dependency_analyzer", "//third_party/utils/src/test:test_util", "@com_google_guava_guava_21_0_with_file//jar", @@ -58,9 +55,6 @@ scala_test( ], unused_dependency_checker_mode = "off", deps = [ - "//external:io_bazel_rules_scala/dependency/scala/scala_compiler", - "//external:io_bazel_rules_scala/dependency/scala/scala_library", - "//external:io_bazel_rules_scala/dependency/scala/scala_reflect", "//third_party/dependency_analyzer/src/main:dependency_analyzer", "//third_party/utils/src/test:test_util", "@org_apache_commons_commons_lang_3_5_without_file//:linkable_org_apache_commons_commons_lang_3_5_without_file", diff --git a/twitter_scrooge/twitter_scrooge.bzl b/twitter_scrooge/twitter_scrooge.bzl index a0217d668..f3968a665 100644 --- a/twitter_scrooge/twitter_scrooge.bzl +++ b/twitter_scrooge/twitter_scrooge.bzl @@ -238,7 +238,6 @@ def _compile_scala( print_compile_time = False, expect_java_output = False, scalac_jvm_flags = [], - scalac = ctx.attr._scalac, ) return JavaInfo( diff --git a/unstable/multiscala/BUILD.bazel b/unstable/multiscala/BUILD.bazel new file mode 100644 index 000000000..0915b96c9 --- /dev/null +++ b/unstable/multiscala/BUILD.bazel @@ -0,0 +1,8 @@ +load(":toolchains.bzl", "create_toolchains") + +create_toolchains() + +toolchain_type( + name = "test_toolchain_type", + visibility = ["//visibility:public"], +) diff --git a/unstable/multiscala/ExternalRepositories.md b/unstable/multiscala/ExternalRepositories.md new file mode 100644 index 000000000..1d9c6a9d2 --- /dev/null +++ b/unstable/multiscala/ExternalRepositories.md @@ -0,0 +1,96 @@ +# External Repositories + +## Summary + +This is a WIP summary of approaches to external dependencies. The text isn't structured at this point. It's a raw capture of current understanding so we can collaborate on the issues and approaches. When we close on that, we can turn this into real user documentation. + +Every scala build is going to have external dependencies. There are toolchain dependencies like the scala compiler and runtime library. There are dependencies used by the `rules_scala` infrastructure like `commons-io` in the scalac driver. Users will have dependencies in their code represented in build files, things like like `com.fasterxml.jackson.core`. + +Some of these dependencies are java and so can be imported fairly straightforwardly. + +Scala dependencies are more complicated in a multiscala environment. Now each abstract dependency is potentially multiple concrete dependencies. For example, abstractly `scalatest` is a single dependency. In a multiscala environment, this can actually mean multiple jars, e.g., `org.scalatest:scalatest_2_11` and `org.scalatest:scalatest_2_12`. We do not want any manual per-version repetition exposed to the user in the common case. + +There are also multiple mechanisms for downloading external dependencies. All these mechanisms end up downloading the desired contents into one or more external bazel repositories. + +The mapping from dependency to label depends on the loader used: e.g., [`jvm.bzl`](https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/repo/jvm.bzl)(and it's variants), [`rules_jvm_external`](https://github.com/bazelbuild/rules_jvm_external), etc. These mechanisms create labels in incompatible ways. Moreover, in many cases, the exact labels used depends on arguments provided by the user. + +Mandating a particular loading mechanism and with particular arguments would provide the ability to uniquely map scala version to label but this restriction is not acceptable. + +There appear to be two options: +1. use pairs of helper macros to produce and consume structured labels +2. use `bind` to create labels in a canonical pattern + +Both of these approaches would rely on a canonical representation of a dependency. Presumably this would be something like `{org}:{artifact}:{version}`. At target creation time, version would be dropped, e.g., `{org}:{artifact}`. The scala version _is not_ included in the canonical representation since it's assume to follow the common scala pattern. (This is actually a bit of a problem for the `org.scala-lang` artifacts that don't follow the common pattern.) + +## Using helper macros + +In essence, for each loader type (and with the user being able to extend), a pair of macros would be defined, each of which takes the canonical format. The loader macro would translate the canonical coordinate to an external repo request, e.g., +``` +"commons-io:commons-io:2.6" +``` +could translate to a macro the calls, approximately, +``` + _scala_maven_import_external( + name = "scalac_rules_commons_io", + artifact = "commons-io:commons-io:2.6", + artifact_sha256 = "f877d304660ac2a142f3865badfc971dec7ed73c747c7f8d5d2f5139ca736513", + licenses = ["notice"], + server_urls = ... + ) +``` +or one that calls +``` +maven_install( + artifacts = [ + "commons-io:commons-io:2.6", + ], + repositories = ... +) +``` +These aren't literal but accurate in direction. + +In the case of a scala dep, they must make loader calls for each coordinate for each version. (FWIW, bazel is smart enough to only download the versions you need for the targets you ask for.) + +The rule-time reference takes the coordinate and returns the label the loader produces. In the cases above, these would be `@scalac_rules_common_io//jar` and `@maven//:commons_io_commons_io`. + +In both cases, the macros needs to know whether the jar is scala-versioned and adjust accordingly. + +This all seems very doable and can, to some extent, be built into the library so it doesn't have to be reflected in every build file. + +Because loading and rule-time reference are so far apart in the structure of bazel, I think it might be necessary to parameterize this behavior per repo by injecting the user choice into the synthetically-created configuration repository. This is not particularly hard or objectionable. + +The bigger concern is if multiple loading techniques are used within the same workspace, e.g., some `jvm.bzl` and some `rules_jvm_external`. It relatively straightfoward to to do this but the simplest approach would require each build file to know which mechanism was used for each dependencies. It's possible to imagine keeping track of the per-coordinate choice in the configuration repo but that might be a bit scary ... + +## Using `bind` + +A potentially straightforward alternative is to simply require `bind`ing whatever label is created by the loader to a canonical label in the external namespace. This separates the loading mechanism from the rule-time consumption mechanism. This simplies consumtion at the expense of loading time work (`bind`ing labels) which, if you have to choose, is the right place to mange the complexity. Among other things, ad hoc corner-cases could be handled relatively easily, more easily than with the previous mechanism. It's also amenable to using multiple loading mechanisms without having to reflect the loader chose in build files. + +It's required that the `external` path is canonically paired with the maven coordinate, e.g., `commons-io:commons-io` would end up at `//external:maven/commons-io/commons-io` no matter how the jar was loaded. + +## Which to use? + +I'm not sure. I don't know if we need to support mixed loaders in a single workspace. If we did, I think I'd tend to lean towards `bind`. + +`bind` is considered bad in many cases and for good reason. Where patterns aren't strongly enforced and/or where it's not actually adding any value (you could easily depend on the loader label because it's well known), using `bind` is a significant increase in complexity. However, here we'd be using a very strict target pattern which, among other things, would make understanding the meaning of th label and searching for references to the label straightforward. + +The alternative, at this point, in the face of supporting multiple loading mechanisms by needing to reflect the mechanism for each dependency in every build file, I find a significant burden for build file writers. + +The alternative of keeping a map of the necessary information in the configuration workspace is potentially viable but I haven't really investigated it. + +## Open issues + +### Handling different versions across different targets + +Do we need this? I suspect we might, for instance if the version of `commons-io` we need for building the toolchain is different than the one a user wants for their own code. I think the answer to this is the idea of a scope. + +This is easy to handle at load time: `rules_jvm_external` has this natively and it can added systemtically as part of the name in `jvm.bzl` repos. + +We would have to figure out how to reflect scopes in rule dependencies since it's not reflected in the normal maven coordinate. + +### Handling different shas for tools that want to pass the shas inline + +`rules_jvm_external` doesn't use this (it puts the shas in a separate out-of-band file in a way that shouldn't affect this work). Other tools like `jvm.bzl` do. Maybe we factor our the shas into a dict and add them at repo call time. + +### Handling multiple references to the same object + +This is essentially the `if not native.existing_rule` issue and I still don't have a handle on what happens (or should happen) when you have reconvergence: where two paths want the same dependency and give it the same label but spec different versions, e.g., protobufs. IIUC, it's possible you could get non-deterministic builds because I think the results would depend on execution order of loads which I think can run concurrently and therefore non-deterministicly. diff --git a/unstable/multiscala/README.md b/unstable/multiscala/README.md new file mode 100644 index 000000000..65d6c58f9 --- /dev/null +++ b/unstable/multiscala/README.md @@ -0,0 +1,93 @@ +# This is a very early work-in-progress. + +It is not only not stable, it's not complete. All the documentation here is as much or more about collaborating on the design as it is on documenting the behavior. In other words, it's highly likely that the behavior does not yet match the documentation. + +## example + +In [`private/example`](private/example) + +## Configuration + +The configuration is currently via a dict of dicts, lists, strings and booleans. It's not yet documented but there is an example in the example [`WORKSPACE`]((private/example/WORKSPACE)). The feeling is that this is ripe for refactoring. There's been a request to support multiple minor versions for the same major version which wouldn't be very hard. Another thing to consider is do we use helper macros to construct this rather than core types. That would be less error prone. Also, it's possible structs might be preferable to dicts since it might make code more succinct and less error prone. + +## Creating targets + +I would like to make the use of multiscala easy ("make easy things simple and hard things possible") + +One goal would be that + +``` +scala_library( + name = "lib", + ... +) + +scala_binary( + name = "app", + deps = [ + ":lib", + ... + ] + ... +) +``` +would act with the current default single version configuration mostly exactly as it does today: it would create a single jar for each target, e.g., `lib.jar` and `app.jar` (but we would probably add some aliases, too, with version suffixes as commmonly seen in maven). + +It would be highly desriable if a a change of configuration to specify two scala versions would not require any changes from the user. In other words, the default behavior would be to build everything against all versions and use arguments to reduce targets, not require them to increase them. At this point, that may not be feasible though close approximation probably are. See below + +So in the example above, with two scala versions configured, a build would create, for example, `lib_2_11.jar`, `lib_2_12.jar`, `app_2_11.jar` and `app_2_12.jar`. The mutliscala code will create the versioned targets based on the version deps. (This is a simplified example. As mentioned, I think we'll end up creating one jar for every version and then a set of aliases to give people the names that they expect.) + +To do this, we'd need to change `scala_library` from a rule to a macro. The macro has access to the configuration (which is why it's an external repo) and can instantiate the necessary targets and aliases. + +I do wonder if folks will consider this _too magic_. I can say that the developers I work with would prefer this to manual copying or having to write a starlark loop themselves for every target. + +## Challenges to supporting multiscala without build file changes + +The primary challenge here is `deps` and `runtime_deps` (and anything else of that ilk). In the example above, version-specific jars of `app` need to depend on the version-specific jars of `lib`. At this point, I don't see any way to do this with a `scala_binary` macro: information about the depended-on target isn't available during loading and it's too late to make dependence changes at analysis. + +So far, the only thing I've come up with is to add `scala_deps` and `scala_runtime_deps` arguments to the `scala_*` macros. The macros can then add the necessary version information to the labels before combining them with the standard, unaltered sets (`deps`, `runtime_deps`) and then running the standard rule to create a target. So to migrate, a user would have to move existing dependencies that need automatic scala version naming to `scala_deps`, leaving non-versioned (java) deps in `deps`. It would be easy enough to add compatibility no-op shims to allow people to future proof while still sticking with stable code though correctness would not be checked. + +## Using defaults and aliases + +When declaring something like the library `lib` above, one can imagine many ways `lib` will be named. In the uniscala mode, this would simply be `lib.jar`. Using the standard scala pattern, one would expect something like `lib_2_12.jar` and `lib_2_11.jar`. + +If we support concurrent minor builds, we can imagine +``` +lib_2_11_10.jar +lib_2_11_12.jar +lib_2_12_10.jar +lib_2_13_1.jar +``` +This could break build environments that ran the multiscala build (for test purposes) but still expected a uniscala-like target, i.e., `lib.jar`. + +The proposed model is that users can optionally configure/ask for defaults. Based on that default, aliases are created that remove version information from names. This could be both a global default but also could be done for each major version. + +So in the example above if we configured a 2.11 default of 12, a 2.12 default of 10, and a global default of 2.12, we would expect +``` +lib_2_11_10.jar +lib_2_11_12.jar +lib_2_11.jar +lib_2_12_10.jar +lib_2_12.jar +lib_2_13_1.jar +lib.jar +``` +We might want a `lib_2.jar` as well, just to be complete. + +Defaults are explicit unless there's only one option, e.g., 2.12.10, above in which case they're implicit. They can be explicitly inhibited with a value of None. + +One thing I noted and I'm not sure about is that AFAICT, bazel implements aliases with copies. That means keeping unneeded aliases takes disk space. Not sure if this is an issue ... + +I suspect, instead, what we might want to do is provide a macro argument that indicates we want a specifc version to be the default, e.g., something like +``` +scala_binary( + name = "my-app", + ... + default_version = "2.12", +) +``` +The macro would alias this and only this version of this target. This would produce far less copies at the expense of requiring users to add this to all rules where they want this behavior. Alternatively, we could have a configuration along the lines of `alias_default_binary = True`. That might be the best trade-off: not aliasing every library target but not requiring the user annotate every binary target. Supporting both would be fairly simple. + +## External Repos + +See [External Repositories](ExternalReposistories.md) diff --git a/unstable/multiscala/configuration.bzl b/unstable/multiscala/configuration.bzl new file mode 100644 index 000000000..4359e6982 --- /dev/null +++ b/unstable/multiscala/configuration.bzl @@ -0,0 +1,165 @@ +"""Macros to configure scala versions and dependencies. + +Creates the configuration repo @io_bazel_rules_scala_configuration. +""" + +load("@bazel_skylib//lib:dicts.bzl", _dicts = "dicts") +load("@bazel_skylib//lib:sets.bzl", _sets = "sets") + +# default configuration: public for user examination (if desired?) +default_configuration = { + "compatability_labels": True, + "default": "2.11", + "repositories": [ + "https://jcenter.bintray.com/", + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], + "scala": { + "2.11": { + "minor": "12", + }, + }, + "scala-parser-combinators": "1.0.4", + "scala-xml": "1.0.5", + "scala_bootstrap_toolchain": { + "scala_test_jvm_flags": [], + "scalac_jvm_flags": [], + "scalacopts": [], + }, + "scala_toolchain": { + "enable_code_coverage_aspect": "off", + "plus_one_deps_mode": "off", + "unused_dependency_checker_mode": "off", + }, + "scalactic": "3.0.5", + "scalatest": "3.0.5", +} + +def _repo_impl(ctx): + ctx.file( + "BUILD.bazel", + content = 'exports_files(["configuration.bzl"])', + executable = False, + ) + ctx.template( + "configuration.bzl", + ctx.attr._template, + substitutions = {"%{STARLARK_STRING}": ctx.attr.starlark_string}, + executable = False, + ) + +_repo = repository_rule( + implementation = _repo_impl, + attrs = { + "starlark_string": attr.string(mandatory = True), + "_template": attr.label( + default = ":private/templates/configuration.bzl.tpl", + ), + }, +) + +def _merge_dicts(*dicts, exclude = None): + """merge multiple dictionaries + + This is a lot like typical map merging but limited by the lack of + recursion in starlark. It will merge values to one level only. + + Args: + *dicts: the list of dicts to merge, sequentially + exclude: a key to exclude from merging (merging may not work) + + """ + + configuration = {} + + for input in dicts: + keys = _sets.make(configuration.keys() + input.keys()) + + if exclude and _sets.contains(keys, exclude): + _sets.remove(keys, exclude) + + keys = _sets.to_list(keys) + + for key in keys: + if key in input: + field_type = type(input[key]) + if field_type == "string" or field_type == "list" or field_type == "bool": + configuration[key] = input[key] + elif field_type == "dict": + configuration[key] = _dicts.add( + configuration.get(key, {}), + input[key], + ) + elif field_type == "NoneType": + configuration.pop(key) + else: + fail([key, field_type]) + + return configuration + +def multiscala_configuration(configuration = default_configuration): + """Primary entry point for configuration. + + Args: + configuration: the configuration the user desires. Defaults to the one distributed. + """ + + configuration = _merge_dicts(default_configuration, configuration) + + # include default (true or false) for each target scala version rather than the selected default + if not "default" in configuration and len(configuration["scala"].keys()) == 1: + configuration = _dicts.add( + configuration, + {"default": configuration["scala"][configuration["scala"].keys()[0]]}, + ) + + # since "scala" is a map key, we need to merge each item rather + # than having a user-specified scala key completely override the + # default. + + scala = {} + + for version in configuration["scala"].keys(): + dict = _merge_dicts(configuration, configuration["scala"][version], exclude = "scala") + + dict["scala"] = version + dict["mvn"] = version # .replace(".", "_") + dict["complete"] = version + "." + dict["minor"] + dict["default"] = True if dict.get("default") == version else False + + dict["bootstrap_toolchain"] = toolchain_label("bootstrap", version) + + scala[version] = dict + + configuration["scala"] = scala + + starlark_string = struct(**configuration).to_json() # .replace(":null,", ":None,") + + _repo( + name = "io_bazel_rules_scala_configuration", + starlark_string = starlark_string, + ) + +def scalac_label(version): + return "//src/java/io/bazel/rulesscala/scalac:scalac_" + version + +def scalatest_runner_label(version): + return "//src/java/io/bazel/rulesscala/scala_test:runner_" + version + +def scalatest_reporter_label(version): + return "//scala/support:test_reporter_" + version # .replace(".", "_") + +def toolchain_label(toolchain, version, in_package = False): + return "{package}{toolchain}_{version}_toolchain".format( + package = "@io_bazel_rules_scala//unstable/multiscala:" if not in_package else "", + toolchain = toolchain, + version = version, # .replace(".", "_"), + ) + +def native_toolchain_label(toolchain, version, in_package = False): + return "{package}native_{toolchain}_{version}_toolchain".format( + package = "@io_bazel_rules_scala//unstable/multiscala:" if not in_package else "", + toolchain = toolchain, + version = version, # .replace(".", "_"), + ) diff --git a/unstable/multiscala/configure.bzl b/unstable/multiscala/configure.bzl new file mode 100644 index 000000000..77452299a --- /dev/null +++ b/unstable/multiscala/configure.bzl @@ -0,0 +1,26 @@ +"""post-configuration generation, complete other actions based on the resulting configuration""" + +load( + "@io_bazel_rules_scala//unstable/multiscala:configuration.bzl", + _native_toolchain_label = "native_toolchain_label", +) +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _configuration = "configuration", +) + +def multiscala_configure(): + _maybe_register_default_toolchains() + +def _maybe_default(): + return _configuration()["scala"][_configuration()["default"]] if "default" in _configuration() else None + +def _maybe_register_default_toolchains(): + version = _maybe_default() + if version: + for toolchain in [ + "bootstrap", + "scala", + "scala_test", + ]: + native.register_toolchains(_native_toolchain_label(toolchain, version["mvn"])) diff --git a/unstable/multiscala/macros.bzl b/unstable/multiscala/macros.bzl new file mode 100644 index 000000000..51fe3240d --- /dev/null +++ b/unstable/multiscala/macros.bzl @@ -0,0 +1,12 @@ +"""multiscala equivalents to scala/scala.bzl + +TBD +""" + +load(":private/macros/scala_binary.bzl", _scala_binary = "scala_binary") +load(":private/macros/scala_library.bzl", _scala_library = "scala_library") +load(":private/macros/scala_test.bzl", _scala_test = "scala_test") + +scala_library = _scala_library +scala_binary = _scala_binary +scala_test = _scala_test diff --git a/unstable/multiscala/multiscala.bzl b/unstable/multiscala/multiscala.bzl new file mode 100644 index 000000000..5c4a60f0f --- /dev/null +++ b/unstable/multiscala/multiscala.bzl @@ -0,0 +1,21 @@ +"""multiscala equivalents to scala/scala.bzl + +TBD +""" + +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _configuration = "configuration", +) +load("@bazel_skylib//lib:dicts.bzl", _dicts = "dicts") +load(":configuration.bzl", _toolchain_label = "toolchain_label") +load("//unstable/multiscala/private:macros/scala_binary.bzl", _scala_binary = "scala_binary") +load("//unstable/multiscala/private:macros/scala_library.bzl", _scala_library = "scala_library") +load("//unstable/multiscala/private:macros/scala_test.bzl", _scala_test = "scala_test") + +# def scala_library(**kwargs): _scala_library(_configuration(), **kwargs) +# def scala_binary(**kwargs): _scala_binary(_configuration(), **kwargs) +# def scala_test(**kwargs): +# _scala_test(**_dicts.add(kwargs, configuration = _configuration())) + +toolchain_label = _toolchain_label diff --git a/unstable/multiscala/private/BUILD.bazel b/unstable/multiscala/private/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/unstable/multiscala/private/example/App.scala b/unstable/multiscala/private/example/App.scala new file mode 100644 index 000000000..79148dace --- /dev/null +++ b/unstable/multiscala/private/example/App.scala @@ -0,0 +1,5 @@ +object App extends scala.App { + def version = scala.util.Properties.versionString + + println(s"hello, world from $version!") +} diff --git a/unstable/multiscala/private/example/AppTest.scala b/unstable/multiscala/private/example/AppTest.scala new file mode 100644 index 000000000..299136072 --- /dev/null +++ b/unstable/multiscala/private/example/AppTest.scala @@ -0,0 +1,10 @@ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class AppTest extends AnyFlatSpec with Matchers { + it should "have a successful test" in { + System.err.println(s"hello, world from ${scala.util.Properties.versionString}!") + + App.version should be (scala.util.Properties.versionString) + } +} diff --git a/unstable/multiscala/private/example/BUILD.bazel b/unstable/multiscala/private/example/BUILD.bazel new file mode 100644 index 000000000..6bddacb4b --- /dev/null +++ b/unstable/multiscala/private/example/BUILD.bazel @@ -0,0 +1,76 @@ +load( + "@io_bazel_rules_scala//scala:scala.bzl", + "scala_binary", + "scala_library", + "scala_test", +) +load( + "@io_bazel_rules_scala//unstable/multiscala:multiscala.bzl", + "toolchain_label", +) + +# default case: builds all configured versions with version suffixes + +scala_library( + name = "library", + srcs = glob( + ["*.scala"], + exclude = ["*Test.scala"], + ), +) + +scala_binary( + name = "app", + main_class = "App", + scala_runtime_deps = [":library"], +) + +scala_test( + name = "test", + srcs = ["AppTest.scala"], + scala_deps = [":library"], +) + +# explict version configuration + +scala_library( + name = "library_with_explict_version", + srcs = ["App.scala"], + scala = "2.12", +) + +scala_binary( + name = "app_with_explict_version", + main_class = "App", + scala = "2.12", + scala_runtime_deps = [":library_with_explict_version"], +) + +scala_test( + name = "test_with_explict_version", + srcs = ["AppTest.scala"], + scala = "2.12", + scala_deps = [":library_with_explict_version"], +) + +# explict toolchain configuration: this disabled multiscala: you're on your own ... + +scala_library( + name = "library_with_explict_toolchain", + srcs = ["App.scala"], + toolchains = [toolchain_label("scala", "2.11")], +) + +scala_binary( + name = "app_with_explict_toolchain", + main_class = "App", + toolchains = [toolchain_label("scala", "2.11")], + runtime_deps = [":library_with_explict_toolchain"], +) + +scala_test( + name = "test_with_explict_toolchain", + srcs = ["AppTest.scala"], + toolchains = [toolchain_label("scala", "2.11")], + deps = [":library_with_explict_toolchain"], +) diff --git a/unstable/multiscala/private/example/WORKSPACE b/unstable/multiscala/private/example/WORKSPACE new file mode 100644 index 000000000..7f1bd8e19 --- /dev/null +++ b/unstable/multiscala/private/example/WORKSPACE @@ -0,0 +1,78 @@ +load("//repositories:bazel_skylib.bzl", "load_bazel_skylib") + +load_bazel_skylib() + +load("//repositories:rules_jvm_external.bzl", "load_rules_jvm_external") + +load_rules_jvm_external() + +load("//repositories:rules_scala.bzl", "load_rules_scala") + +load_rules_scala() + +load("//repositories:rules_proto.bzl", "load_rules_proto") + +load_rules_proto() + +load("//repositories:rules_python.bzl", "load_rules_python") + +load_rules_python() + +load( + "@io_bazel_rules_scala//unstable/multiscala:configuration.bzl", + "multiscala_configuration", +) + +multiscala_configuration(configuration = { + "default": "2.12", + "scala": { + "2.11": { + "minor": "12", + }, + "2.12": { + "minor": "10", + }, + "2.13": { + "minor": "1", + "scala_bootstrap_toolchain": { + "scalacopts": [ + "-deprecation:true", + "-encoding", + "UTF-8", + "-feature", + "-unchecked", + "-Xfatal-warnings", + "-Xsource:2.13", + ], + }, + }, + }, + "scala-parser-combinators": "1.1.2", + "scala-xml": "1.2.0", + "scala_bootstrap_toolchain": { + "scalacopts": [ + "-deprecation:true", + "-encoding", + "UTF-8", + "-feature", + "-unchecked", + "-Xfatal-warnings", + "-Xfuture", + "-Xsource:2.12", + "-Ypartial-unification", + ], + }, + "scala_toolchain": { + "unused_dependency_checker_mode": "error", + }, + "scalatest": "3.1.0", + "scalactic": "3.1.0", +}) + +load("@io_bazel_rules_scala//unstable/multiscala:repositories.bzl", "create_repositories") + +create_repositories() + +load("@io_bazel_rules_scala//unstable/multiscala:configure.bzl", "multiscala_configure") + +multiscala_configure() diff --git a/unstable/multiscala/private/example/repositories/BUILD.bazel b/unstable/multiscala/private/example/repositories/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/unstable/multiscala/private/example/repositories/bazel_skylib.bzl b/unstable/multiscala/private/example/repositories/bazel_skylib.bzl new file mode 100644 index 000000000..e6477bed7 --- /dev/null +++ b/unstable/multiscala/private/example/repositories/bazel_skylib.bzl @@ -0,0 +1,11 @@ +"""load skylib""" + +load(":tools.bzl", _github_release = "github_release") + +def load_bazel_skylib(): + _github_release( + name = "bazel_skylib", + sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + repository = "bazelbuild/bazel-skylib", + release = "1.0.2", + ) diff --git a/unstable/multiscala/private/example/repositories/rules_jvm_external.bzl b/unstable/multiscala/private/example/repositories/rules_jvm_external.bzl new file mode 100644 index 000000000..d68a0e2c4 --- /dev/null +++ b/unstable/multiscala/private/example/repositories/rules_jvm_external.bzl @@ -0,0 +1,11 @@ +"""load rules_jvm_external""" + +load(":tools.bzl", _github_archive = "github_archive") + +def load_rules_jvm_external(): + _github_archive( + name = "rules_jvm_external", + repository = "bazelbuild/rules_jvm_external", + sha256 = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a", + tag = "3.0", + ) diff --git a/unstable/multiscala/private/example/repositories/rules_proto.bzl b/unstable/multiscala/private/example/repositories/rules_proto.bzl new file mode 100644 index 000000000..bddf5cc67 --- /dev/null +++ b/unstable/multiscala/private/example/repositories/rules_proto.bzl @@ -0,0 +1,11 @@ +"""load rules_proto: needed by protobuf repo""" + +load(":tools.bzl", _github_archive = "github_archive") + +def load_rules_proto(): + _github_archive( + name = "rules_proto", + repository = "bazelbuild/rules_proto", + sha256 = "62847ac7740865d73a2c8199be292bba913d62e79084442f3e829c3058a25e64", + tag = "d7666ec475c1f8d4a6803cbc0a0b6b4374360868", + ) diff --git a/unstable/multiscala/private/example/repositories/rules_python.bzl b/unstable/multiscala/private/example/repositories/rules_python.bzl new file mode 100644 index 000000000..9ab16594c --- /dev/null +++ b/unstable/multiscala/private/example/repositories/rules_python.bzl @@ -0,0 +1,11 @@ +"""load rules_python: needed by protobuf repo""" + +load(":tools.bzl", _github_archive = "github_archive") + +def load_rules_python(): + _github_archive( + name = "rules_python", + repository = "bazelbuild/rules_python", + sha256 = "7d64815f4b22400bed0f1b9da663037e1578573446b7bc78f20f24b2b5459bb9", + tag = "38f86fb55b698c51e8510c807489c9f4e047480e", + ) diff --git a/unstable/multiscala/private/example/repositories/rules_scala.bzl b/unstable/multiscala/private/example/repositories/rules_scala.bzl new file mode 100644 index 000000000..d02164e0c --- /dev/null +++ b/unstable/multiscala/private/example/repositories/rules_scala.bzl @@ -0,0 +1,7 @@ +"""link back to parent rules_scala repo""" + +def load_rules_scala(): + native.local_repository( + name = "io_bazel_rules_scala", + path = "../../../..", + ) diff --git a/unstable/multiscala/private/example/repositories/tools.bzl b/unstable/multiscala/private/example/repositories/tools.bzl new file mode 100644 index 000000000..88f628714 --- /dev/null +++ b/unstable/multiscala/private/example/repositories/tools.bzl @@ -0,0 +1,28 @@ +"""helpers for to remove http_archive boilerplate + +N.B.: doesn't currently include the bazel mirros +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def github_release(name, repository, release, sha256): + (org, repo) = repository.split("/") + http_archive( + name = name, + sha256 = sha256, + urls = [ + "https://github.com/{repository}/releases/download/{release}/{repo}-{release}.tar.gz".format(repository = repository, repo = repo, release = release), + ], + ) + +def github_archive(name, repository, sha256, tag): + (org, repo) = repository.split("/") + without_v = tag[1:] if tag.startswith("v") else tag + http_archive( + name = name, + sha256 = sha256, + strip_prefix = "{repo}-{without_v}".format(repo = repo, without_v = without_v), + urls = [ + "https://github.com/{repository}/archive/{tag}.zip".format(repository = repository, tag = tag), + ], + ) diff --git a/unstable/multiscala/private/macros/scala_binary.bzl b/unstable/multiscala/private/macros/scala_binary.bzl new file mode 100644 index 000000000..ac1091181 --- /dev/null +++ b/unstable/multiscala/private/macros/scala_binary.bzl @@ -0,0 +1,61 @@ +"""multiscala equivalents to scala/scala.bzl + +TBD +""" + +load("@bazel_skylib//lib:dicts.bzl", _dicts = "dicts") +load( + "//unstable/multiscala:configuration.bzl", + _toolchain_label = "toolchain_label", +) +load( + "//scala/private:rules/scala_binary.bzl", + _uniscala_scala_binary = "scala_binary", +) +load( + "//unstable/multiscala/private:macros/tools.bzl", + _combine_kwargs = "combine_kwargs", + _remove_toolchains = "remove_toolchains", + _target_versions = "target_versions", +) + +_binary_suffixes = ["", "_deploy.jar"] + +def _create_scala_binary(version, **kwargs): + kwargs = _remove_toolchains(kwargs, version) + kwargs = _combine_kwargs(kwargs, version["mvn"]) + kwargs.update( + toolchains = [_toolchain_label("scala", version["mvn"])], + ) + + # print(kwargs) + _uniscala_scala_binary(**kwargs) + +def scala_binary( + scala_deps = [], + scala_runtime_deps = [], + deps = [], + runtime_deps = [], + scala = None, + **kwargs): + """create a multi-scala binary + + Args: + scala_deps: deps that require scala version naming + scala_runtime_deps: deps that require scala version naming + deps: deps that do not require scala version changes + runtime_deps: runtime_deps that do not require scala version changes + scala: verisons of scala to build for + **kwargs: standard scala_binary arguments + """ + kwargs = _dicts.add(kwargs) + kwargs.update( + scala = scala, + deps = deps, + runtime_deps = runtime_deps, + scala_deps = scala_deps, + scala_runtime_deps = scala_runtime_deps, + ) + + for version in _target_versions(kwargs): + _create_scala_binary(version, **kwargs) diff --git a/unstable/multiscala/private/macros/scala_library.bzl b/unstable/multiscala/private/macros/scala_library.bzl new file mode 100644 index 000000000..f87364355 --- /dev/null +++ b/unstable/multiscala/private/macros/scala_library.bzl @@ -0,0 +1,60 @@ +"""multiscala equivalents to scala/scala.bzl + +TBD +""" + +load("@bazel_skylib//lib:dicts.bzl", _dicts = "dicts") +load( + "@io_bazel_rules_scala//scala/private:rules/scala_library.bzl", + _uniscala_scala_library = "scala_library", + # _uniscala_scala_library_for_plugin_bootstrapping = "scala_library_for_plugin_bootstrapping", + # _uniscala_scala_library_suite = "scala_library_suite", + # _uniscala_scala_macro_library = "scala_macro_library", +) +load( + "//unstable/multiscala:configuration.bzl", + _toolchain_label = "toolchain_label", +) +load( + "//unstable/multiscala/private:macros/tools.bzl", + _combine_kwargs = "combine_kwargs", + _remove_toolchains = "remove_toolchains", + _target_versions = "target_versions", +) + +def _create_scala_library(version, **kwargs): + kwargs = _remove_toolchains(kwargs, version) + kwargs = _combine_kwargs(kwargs, version["mvn"]) + _uniscala_scala_library( + toolchains = [_toolchain_label("scala", version["mvn"])], + **kwargs + ) + +def scala_library( + scala_deps = [], + scala_runtime_deps = [], + deps = [], + runtime_deps = [], + scala = None, + **kwargs): + """create a multi-scala library + + Args: + scala_deps: deps that require scala version naming + scala_runtime_deps: deps that require scala version naming + deps: deps that do not require scala version changes + runtime_deps: runtime_deps that do not require scala version changes + scala: verisons of scala to build for + **kwargs: standard scala_library arguments + """ + kwargs = _dicts.add(kwargs) + kwargs.update( + scala = scala, + deps = deps, + runtime_deps = runtime_deps, + scala_deps = scala_deps, + scala_runtime_deps = scala_runtime_deps, + ) + + for version in _target_versions(kwargs): + _create_scala_library(version, **kwargs) diff --git a/unstable/multiscala/private/macros/scala_test.bzl b/unstable/multiscala/private/macros/scala_test.bzl new file mode 100644 index 000000000..3055dec1a --- /dev/null +++ b/unstable/multiscala/private/macros/scala_test.bzl @@ -0,0 +1,59 @@ +"""multiscala equivalents to scala/scala.bzl + +TBD +""" + +load("@bazel_skylib//lib:dicts.bzl", _dicts = "dicts") +load( + "@io_bazel_rules_scala//scala/private:rules/scala_test.bzl", + _uniscala_scala_test = "scala_test", +) +load( + "//unstable/multiscala:configuration.bzl", + _toolchain_label = "toolchain_label", +) +load( + "//unstable/multiscala/private:macros/tools.bzl", + _combine_kwargs = "combine_kwargs", + _remove_toolchains = "remove_toolchains", + _target_versions = "target_versions", +) + +def _create_scala_test(version, **kwargs): + kwargs = _remove_toolchains(kwargs, version) + kwargs["toolchains"] = [ + _toolchain_label("scala", version["mvn"]), + _toolchain_label("scala_test", version["mvn"]), + ] + kwargs = _combine_kwargs(kwargs, version["mvn"]) + _uniscala_scala_test(**kwargs) + +def scala_test( + scala_deps = [], + scala_runtime_deps = [], + deps = [], + runtime_deps = [], + scala = None, + **kwargs): + """create a multi-scala test + + Args: + scala_deps: deps that require scala version naming + scala_runtime_deps: deps that require scala version naming + deps: deps that do not require scala version changes + runtime_deps: runtime_deps that do not require scala version changes + scala: verisons of scala to build for + **kwargs: standard scala_test arguments + """ + + kwargs = _dicts.add(kwargs) + kwargs.update( + scala = scala, + deps = deps, + runtime_deps = runtime_deps, + scala_deps = scala_deps, + scala_runtime_deps = scala_runtime_deps, + ) + + for version in _target_versions(kwargs): + _create_scala_test(version, **kwargs) diff --git a/unstable/multiscala/private/macros/tools.bzl b/unstable/multiscala/private/macros/tools.bzl new file mode 100644 index 000000000..7d506a07d --- /dev/null +++ b/unstable/multiscala/private/macros/tools.bzl @@ -0,0 +1,117 @@ +"""multiscala equivalents to scala/scala.bzl + +TBD +""" + +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _configuration = "configuration", + _versions = "versions", +) +load("@bazel_skylib//lib:dicts.bzl", _dicts = "dicts") +load("@rules_jvm_external//:defs.bzl", _maven_install = "maven_install") +load( + "//unstable/multiscala:configuration.bzl", + _toolchain_label = "toolchain_label", +) + +# FIXME: rename +def remove_toolchains(kwargs, version): + """check and remove toolchain from kwargs; passed explicitly + + Args: + kwargs: complete set of arguments passed to the macro + version: scala version being targetted + Returns: + New kwargs dict + """ + + kwargs = _dicts.add(kwargs) + kwargs["scala_suffixes"] = True + if "toolchains" in kwargs: + if kwargs["toolchains"] != [_toolchain_label("scala", version["mvn"])]: + fail([kwargs["toolchains"], [_toolchain_label("scala", version["mvn"])]]) + kwargs.pop("toolchains") + kwargs["scala_suffixes"] = False + if "scala" in kwargs: + kwargs.pop("scala") + return kwargs + +def combine_kwargs( + kwargs, + mvn_version): + """"combine adapted scala depedence list with native dependence list + + Args: + kwargs: complete set of args passed to macro + mvn_version: scala version being targetted + + Returns: + Updated kwargs + """ + + kwargs = _dicts.add(kwargs) + + # print("a", kwargs) + kwargs = _combine_deps("runtime_deps", kwargs, mvn_version) + kwargs = _combine_deps("deps", kwargs, mvn_version) + if kwargs["scala_suffixes"]: + kwargs["name"] = kwargs["name"] + "_" + mvn_version + kwargs.pop("scala_deps") + kwargs.pop("scala_runtime_deps") + kwargs.pop("scala_suffixes") + + # print("b", kwargs) + return kwargs + +def _combine_deps( + dep_name, + kwargs, + mvn_version): + kwargs = _dicts.add(kwargs) + new_deps = kwargs[dep_name][:] + for dep in kwargs["scala_" + dep_name]: + if kwargs["scala_suffixes"]: + new_deps.append(dep + "_" + mvn_version) + else: + new_deps.append(dep) + kwargs[dep_name] = new_deps + return kwargs + +def target_versions(kwargs): + """return scala versions that should be targetted based on kwargs + + Args: + kwargs: complete set of args passed to macro + + Returns: + list of versions that should be targetted +""" + if "toolchains" in kwargs: + toolchains = kwargs["toolchains"] + if len(toolchains) != 1: + fail("multiple toolchains not supported yet: %s" % toolchains) + (_, target) = toolchains[0].split(":") + version = target.split("_") + version = version[1:] + version = ".".join(version[:-1]) + return [_configuration()["scala"][version]] + + if "scala" in kwargs: + versions = kwargs["scala"] + + if type(versions) == "NoneType": + return _versions() + + if type(versions) == "string": + versions = [versions] + + return [_configuration()["scala"][version] for version in versions] + + return _versions() + +def maven_install(**kwargs): + _maven_install(**kwargs) + +def scala_maven_install(): + fail() diff --git a/unstable/multiscala/private/templates/configuration.bzl.tpl b/unstable/multiscala/private/templates/configuration.bzl.tpl new file mode 100644 index 000000000..1c4e5ab97 --- /dev/null +++ b/unstable/multiscala/private/templates/configuration.bzl.tpl @@ -0,0 +1,24 @@ +# -*- mode: python -*- + +# load("@io_bazel_rules_scala//unstable/multiscala:macros.bzl", "scala_library") + +def _from_json(): + # starlark vs json ... + + true = True + false = False + null = None + + return %{STARLARK_STRING} + +_configuration = _from_json() + +def configuration(): return _configuration + +def multiscala_enabled(): return True + +def versions(): + return _configuration["scala"].values() + +def versioned_name(name, version): + return name + "_" + version["mvn"] diff --git a/unstable/multiscala/repositories.bzl b/unstable/multiscala/repositories.bzl new file mode 100644 index 000000000..064dcdd49 --- /dev/null +++ b/unstable/multiscala/repositories.bzl @@ -0,0 +1,119 @@ +"""macros to load all necessary repos + +Only public function is `create_repositories` + +Determines all necessary dependences from configuration and loads them. + +All loading via rules_jvm_external. + +If configuration asks for compatiblity labels, `bind`s aliases. + +Includes helpers to reduce boilerplate for github archives and artifact naming. +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive") +load("@io_bazel_rules_scala_configuration//:configuration.bzl", _configuration = "configuration", _versions = "versions") +load("@rules_jvm_external//:defs.bzl", _artifact = "artifact") +load("//unstable/multiscala/private:macros/tools.bzl", _maven_install = "maven_install") + +def _github_archive(name, repository, sha256, tag): + (org, repo) = repository.split("/") + without_v = tag[1:] if tag.startswith("v") else tag + _http_archive( + name = name, + sha256 = sha256, + strip_prefix = "{repo}-{without_v}".format(repo = repo, without_v = without_v), + urls = [ + "https://github.com/{repository}/archive/{tag}.zip".format(repository = repository, tag = tag), + ], + ) + +def _maven_install_artifacts(artifacts): + return artifacts.keys() + +def _bind_default_labels(repository_name, artifacts): + for artifact in artifacts.items(): + (mvn, label) = artifact + without_version = ":".join(mvn.split(":")[0:2]) + native.bind( + name = label, + actual = _artifact(without_version, repository_name = repository_name), + ) + +def create_repositories(): + """create all necessary repos""" + _create_protobuf() + _create_maven_installed_repos() + +def _create_protobuf(): + _github_archive( + name = "com_google_protobuf", + sha256 = "e4f8bedb19a93d0dccc359a126f51158282e0b24d92e0cad9c76a9699698268d", + repository = "protocolbuffers/protobuf", + tag = "v3.11.2", + ) + + # N.B.: could use protobuf/protobuf_deps.bzl + + _http_archive( + name = "zlib", + build_file = "@com_google_protobuf//:third_party/zlib.BUILD", + sha256 = "629380c90a77b964d896ed37163f5c3a34f6e6d897311f1df2a7016355c45eff", + strip_prefix = "zlib-1.2.11", + urls = ["https://github.com/madler/zlib/archive/v1.2.11.tar.gz"], + ) + + if _configuration()["compatability_labels"]: + native.bind( + name = "io_bazel_rules_scala/dependency/com_google_protobuf/protobuf_java", + actual = "@com_google_protobuf//:protobuf_java", + ) + +def _scala_artifact(version, scala_coordinate): + components = scala_coordinate.split(":") + if len(components) == 2: + components.append("{" + components[1] + "}") + (org, artifact, artifact_version) = components + java_coordinate = ":".join([org, artifact + "_" + version["mvn"], artifact_version]) + return java_coordinate.format(**version) + +def _create_maven_installed_repos(): + for version in _versions(): + repository_name = "io_bazel_rules_scala_" + version["scala"].replace(".", "_") + + artifacts = { + "org.scala-lang:scala-compiler:" + version["complete"]: "io_bazel_rules_scala/dependency/scala/scala_compiler", + "org.scala-lang:scala-library:" + version["complete"]: "io_bazel_rules_scala/dependency/scala/scala_library", + "org.scala-lang:scala-reflect:" + version["complete"]: "io_bazel_rules_scala/dependency/scala/scala_reflect", + _scala_artifact(version, "org.scala-lang.modules:scala-xml"): "io_bazel_rules_scala/dependency/scala/scala_xml", + _scala_artifact(version, "org.scalatest:scalatest"): "io_bazel_rules_scala/dependency/scala/scalatest/scalatest", + _scala_artifact(version, "org.scalactic:scalactic"): "io_bazel_rules_scala/dependency/scala/scalactic/scalactic", + } + + _maven_install( + name = repository_name, + artifacts = _maven_install_artifacts(artifacts), + repositories = version["repositories"], + ) + + if version["default"] and version["compatability_labels"]: + _bind_default_labels(repository_name, artifacts) + + if version["default"] and version["compatability_labels"]: + native.bind( + name = "io_bazel_rules_scala/dependency/scalatest/scalatest", + actual = "@io_bazel_rules_scala//scala/scalatest:scalatest", + ) + + java_artifacts = { + "com.google.guava:guava:21.0": "io_bazel_rules_scala/dependency/scala/guava", + "commons-io:commons-io:2.6": "io_bazel_rules_scala/dependency/scalac_rules_commons_io", + } + + _maven_install( + name = "io_bazel_rules_scala_scalac", + artifacts = _maven_install_artifacts(java_artifacts), + repositories = _configuration()["repositories"], + ) + if _configuration()["compatability_labels"]: + _bind_default_labels("io_bazel_rules_scala_scalac", java_artifacts) diff --git a/unstable/multiscala/repositories/rules_jvm_external.bzl b/unstable/multiscala/repositories/rules_jvm_external.bzl new file mode 100644 index 000000000..c83079b28 --- /dev/null +++ b/unstable/multiscala/repositories/rules_jvm_external.bzl @@ -0,0 +1,23 @@ +"""load rules_jvm_external""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def _github_archive(name, repository, sha256, tag): + (org, repo) = repository.split("/") + without_v = tag[1:] if tag.startswith("v") else tag + http_archive( + name = name, + sha256 = sha256, + strip_prefix = "{repo}-{without_v}".format(repo = repo, without_v = without_v), + urls = [ + "https://github.com/{repository}/archive/{tag}.zip".format(repository = repository, tag = tag), + ], + ) + +def load_rules_jvm_external(): + _github_archive( + name = "rules_jvm_external", + repository = "bazelbuild/rules_jvm_external", + sha256 = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a", + tag = "3.0", + ) diff --git a/unstable/multiscala/rules_jvm_external.bzl b/unstable/multiscala/rules_jvm_external.bzl new file mode 100644 index 000000000..e69de29bb diff --git a/unstable/multiscala/toolchains.bzl b/unstable/multiscala/toolchains.bzl new file mode 100644 index 000000000..c6b1f6d87 --- /dev/null +++ b/unstable/multiscala/toolchains.bzl @@ -0,0 +1,137 @@ +"""create toolchains required by configuration""" + +load( + "@io_bazel_rules_scala//scala:scala_toolchain.bzl", + _scala_toolchain_rule = "scala_toolchain", +) +load( + "@io_bazel_rules_scala//scala:bootstrap_toolchain.bzl", + _bootstrap_toolchain_rule = "bootstrap_toolchain", +) +load( + "@io_bazel_rules_scala//scala:scala_test_toolchain.bzl", + _scala_test_toolchain_rule = "scala_test_toolchain", +) +load( + "@io_bazel_rules_scala_configuration//:configuration.bzl", + _versions = "versions", +) +load( + "@rules_jvm_external//:defs.bzl", + _artifact = "artifact", +) +load( + ":configuration.bzl", + _native_toolchain_label = "native_toolchain_label", + _scalac_label = "scalac_label", + _scalatest_reporter_label = "scalatest_reporter_label", + _scalatest_runner_label = "scalatest_runner_label", + _toolchain_label = "toolchain_label", +) + +def create_toolchains(): + _create_all_toolchains() + +def _create_all_toolchains(): + for version in _versions(): + _create_version_toolchains(version) + +def _create_version_toolchains(version): + _create_bootstrap_toolchain(version) + _create_scala_toolchain(version) + _create_scala_test_toolchain(version) + +def _create_bootstrap_toolchain(version): + mvn = version["mvn"] + + name = _toolchain_label("bootstrap", mvn, in_package = True) + + attrs = {} + + attrs["name"] = name + attrs["visibility"] = ["//visibility:public"] + + repository_name = "io_bazel_rules_scala_" + version["scala"].replace(".", "_") + + library = _artifact("org.scala-lang:scala-library", repository_name = repository_name) + compiler = _artifact("org.scala-lang:scala-compiler", repository_name = repository_name) + reflect = _artifact("org.scala-lang:scala-reflect", repository_name = repository_name) + attrs["classpath"] = [library, reflect] + attrs["macro_classpath"] = [library, reflect] + attrs["repl_classpath"] = [compiler, library, reflect] + + _bootstrap_toolchain_rule(**attrs) + + native.toolchain( + name = _native_toolchain_label("bootstrap", version["mvn"], in_package = True), + toolchain = name, + toolchain_type = "@io_bazel_rules_scala//scala:bootstrap_toolchain_type", + visibility = ["//visibility:public"], + ) + +_scala_toolchain_attrs = [ + "scalacopts", + "scalac_provider_attr", + "unused_dependency_checker_mode", + "plus_one_deps_mode", + "enable_code_coverage_aspect", + "scalac_jvm_flags", + "scala_test_jvm_flags", +] + +def _create_scala_toolchain(version): + name = _toolchain_label("scala", version["mvn"], in_package = True) + + attrs = {} + + for attr in _scala_toolchain_attrs: + if attr in version["scala_toolchain"]: + attrs[attr] = version["scala_toolchain"][attr] + + attrs["name"] = name + attrs["visibility"] = ["//visibility:public"] + attrs["scalac"] = _scalac_label(version["mvn"]) + + _scala_toolchain_rule(**attrs) + + native.toolchain( + name = _native_toolchain_label("scala", version["mvn"], in_package = True), + toolchain = name, + toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type", + visibility = ["//visibility:public"], + ) + +def _scala_artifact(version, scala_coordinate): + components = scala_coordinate.split(":") + if len(components) == 2: + components.append("{" + components[1] + "}") + (org, artifact, artifact_version) = components + java_coordinate = ":".join([org, artifact + "_" + version["scala"], artifact_version]) + return java_coordinate.format(**version) + +def _create_scala_test_toolchain(version): + mvn = version["mvn"] + + name = _toolchain_label("scala_test", mvn, in_package = True) + + attrs = {} + + attrs["name"] = name + attrs["visibility"] = ["//visibility:public"] + + repository_name = "io_bazel_rules_scala_" + version["scala"].replace(".", "_") + + scalatest = _artifact("org.scalatest:scalatest_" + version["mvn"], repository_name = repository_name) + scalactic = _artifact("org.scalactic:scalactic_" + version["mvn"], repository_name = repository_name) + attrs["deps"] = [scalatest, scalactic] + attrs["reporter"] = _scalatest_reporter_label(version["mvn"]) + attrs["runner"] = _scalatest_runner_label(version["mvn"]) + + _scala_test_toolchain_rule(**attrs) + + native.toolchain( + name = _native_toolchain_label("scala_test", version["mvn"], in_package = True), + toolchain = name, + toolchain_type = "@io_bazel_rules_scala//scala:scala_test_toolchain_type", + visibility = ["//visibility:public"], + )