diff --git a/.bazelversion b/.bazelversion index 1545d9665..ccbccc3dc 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -3.5.0 +2.2.0 diff --git a/scala/BUILD b/scala/BUILD index 9485a98ec..a765c1bbb 100644 --- a/scala/BUILD +++ b/scala/BUILD @@ -1,5 +1,5 @@ load("@rules_java//java:defs.bzl", "java_import", "java_library") -load("//scala:scala_toolchain.bzl", "scala_toolchain") +load("//scala:scala_toolchain.bzl", "scala_toolchain", "scala_deps_toolchain", "export_scala_toolchain_deps") load("//scala:providers.bzl", "declare_deps_provider") toolchain_type( @@ -7,6 +7,26 @@ toolchain_type( visibility = ["//visibility:public"], ) +toolchain_type( + name = "deps_toolchain_type", + visibility = ["//visibility:public"], +) + +scala_deps_toolchain( + name = "default_deps_toolchain_impl", + dep_providers = [ + ":scalajs_linker_provider", + ], + visibility = ["//visibility:public"], +) + +toolchain( + name = "default_deps_toolchain", + toolchain = ":default_deps_toolchain_impl", + toolchain_type = "@io_bazel_rules_scala//scala:deps_toolchain_type", + visibility = ["//visibility:public"], +) + scala_toolchain( name = "default_toolchain_impl", scalacopts = [], @@ -118,3 +138,19 @@ declare_deps_provider( visibility = ["//visibility:public"], deps = ["//external:io_bazel_rules_scala/dependency/scala/parser_combinators"], ) + +declare_deps_provider( + name = "scalajs_linker_provider", + deps_id = "scalajs_linker_deps", + visibility = ["//visibility:public"], + deps = [ + "//external:io_bazel_rules_scala/dependency/scala_js/scalajs_ir", + "//external:io_bazel_rules_scala/dependency/scala_js/scalajs_linker" + ], +) + +export_scala_toolchain_deps( + name = "scalajs_linker_deps", + deps_id = "scalajs_linker_deps", + visibility = ["//visibility:public"], +) diff --git a/scala/private/common_attributes.bzl b/scala/private/common_attributes.bzl index 9a7046e0f..a50885d31 100644 --- a/scala/private/common_attributes.bzl +++ b/scala/private/common_attributes.bzl @@ -124,3 +124,4 @@ resolve_deps = { allow_files = False, ), } + diff --git a/scala/private/macros/scala_repositories.bzl b/scala/private/macros/scala_repositories.bzl index cb062fbcd..53f819446 100644 --- a/scala/private/macros/scala_repositories.bzl +++ b/scala/private/macros/scala_repositories.bzl @@ -85,6 +85,8 @@ def scala_repositories( "io_bazel_rules_scala_scalactic", "io_bazel_rules_scala_scala_xml", "io_bazel_rules_scala_scala_parser_combinators", + "io_bazel_rules_scala_scalajs_ir", + "io_bazel_rules_scala_scalajs_linker", ], maven_servers = _default_maven_server_urls(), fetch_sources = fetch_sources, @@ -131,3 +133,13 @@ def scala_repositories( name = "io_bazel_rules_scala/dependency/scala/scalactic/scalactic", actual = "@io_bazel_rules_scala_scalactic", ) + + native.bind( + name = "io_bazel_rules_scala/dependency/scala_js/scalajs_ir", + actual = "@io_bazel_rules_scala_scalajs_ir", + ) + + native.bind( + name = "io_bazel_rules_scala/dependency/scala_js/scalajs_linker", + actual = "@io_bazel_rules_scala_scalajs_linker", + ) diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl index 5be2e2baf..65dbbbe12 100644 --- a/scala/private/phases/phase_compile.bzl +++ b/scala/private/phases/phase_compile.bzl @@ -17,6 +17,7 @@ load( _compile_scala = "compile_scala", _expand_location = "expand_location", ) +load("@io_bazel_rules_scala//scala/private/toolchain_deps:toolchain_deps.bzl", "find_deps_info_on") load(":resources.bzl", _resource_paths = "paths") def phase_compile_binary(ctx, p): @@ -41,6 +42,25 @@ def phase_compile_library(ctx, p): ) return _phase_compile_default(ctx, p, args) +def phase_compile_scalajs_library(ctx, p): + scalajs_compiler_plugin = find_deps_info_on( + ctx, + "@io_bazel_rules_scala//scala:toolchain_type", + "scalajs_compiler_plugin", # TODO: make global? + ).deps + + args = struct( + plugins = scalajs_compiler_plugin, + srcjars = p.collect_srcjars, + unused_dependency_checker_ignored_targets = [ + target.label + for target in p.scalac_provider.default_classpath + ctx.attr.exports + + ctx.attr.unused_dependency_checker_ignored_targets + ], + ) + + return _phase_compile_default(ctx, p, args) + def phase_compile_library_for_plugin_bootstrapping(ctx, p): args = struct( buildijar = ctx.attr.build_ijar, @@ -110,6 +130,7 @@ def _phase_compile_default(ctx, p, _args = struct()): _args.buildijar if hasattr(_args, "buildijar") else True, _args.implicit_junit_deps_needed_for_java_compilation if hasattr(_args, "implicit_junit_deps_needed_for_java_compilation") else [], unused_dependency_checker_ignored_targets = _args.unused_dependency_checker_ignored_targets if hasattr(_args, "unused_dependency_checker_ignored_targets") else [], + plugins = _args.plugins if hasattr(_args, "plugins") else ctx.attr.plugins, ) def _phase_compile( @@ -119,7 +140,8 @@ def _phase_compile( buildijar, # TODO: generalize this hack implicit_junit_deps_needed_for_java_compilation, - unused_dependency_checker_ignored_targets): + unused_dependency_checker_ignored_targets, + plugins): manifest = ctx.outputs.manifest jars = p.collect_jars.compile_jars rjars = p.collect_jars.transitive_runtime_jars @@ -141,6 +163,7 @@ def _phase_compile( deps_providers, default_classpath, unused_dependency_checker_ignored_targets, + plugins ) # TODO: simplify the return values and use provider @@ -165,7 +188,8 @@ def _compile_or_empty( dependency_info, deps_providers, default_classpath, - unused_dependency_checker_ignored_targets): + unused_dependency_checker_ignored_targets, + plugins): # We assume that if a srcjar is present, it is not empty if len(ctx.files.srcs) + len(srcjars.to_list()) == 0: _build_nosrc_jar(ctx) @@ -195,7 +219,7 @@ def _compile_or_empty( jars, all_srcjars, transitive_compile_jars, - ctx.attr.plugins, + plugins, ctx.attr.resource_strip_prefix, ctx.files.resources, ctx.files.resource_jars, diff --git a/scala/private/phases/phase_link_scalajs.bzl b/scala/private/phases/phase_link_scalajs.bzl new file mode 100644 index 000000000..1995b5ad3 --- /dev/null +++ b/scala/private/phases/phase_link_scalajs.bzl @@ -0,0 +1,39 @@ +# +# PHASE: link scalajs jars +# +# +# +def _colon_paths(data): + return ":".join([ + f.path + for f in sorted(data) + ]) + +def phase_link_scalajs(ctx, p): + """ + Links scala.js IR into vanilla javascript. + """ + + output_js = ctx.outputs.js + runtime_jars = sorted(p.collect_jars.transitive_runtime_jars.to_list()) + + worker_content = "{output}\n{main_class}\n{method}\n{include_jars}".format( + output = output_js.path, + main_class = getattr(ctx.attr, "main_class", ""), + method = getattr(ctx.attr, "method", ""), + include_jars = _colon_paths(runtime_jars), + ) + argfile = ctx.actions.declare_file( + "%s_worker_input" % ctx.label.name, + sibling = output_js, + ) + ctx.actions.write(output = argfile, content = worker_content) + ctx.actions.run( + inputs = runtime_jars + [argfile], + outputs = [output_js], + executable = ctx.attr._scalajs_linker.files_to_run, + mnemonic = "ScalaJSLink", + progress_message = "Linking scala.js file: %s" % ctx.label, + execution_requirements = {"supports-workers": "1"}, + arguments = ["@" + argfile.path], + ) diff --git a/scala/private/phases/phases.bzl b/scala/private/phases/phases.bzl index 3f7ff7f06..16a10b25b 100644 --- a/scala/private/phases/phases.bzl +++ b/scala/private/phases/phases.bzl @@ -33,6 +33,7 @@ load( _phase_compile_common = "phase_compile_common", _phase_compile_junit_test = "phase_compile_junit_test", _phase_compile_library = "phase_compile_library", + _phase_compile_scalajs_library = "phase_compile_scalajs_library", _phase_compile_library_for_plugin_bootstrapping = "phase_compile_library_for_plugin_bootstrapping", _phase_compile_macro_library = "phase_compile_macro_library", _phase_compile_repl = "phase_compile_repl", @@ -61,6 +62,7 @@ load( ) load("@io_bazel_rules_scala//scala/private:phases/phase_declare_executable.bzl", _phase_declare_executable = "phase_declare_executable") load("@io_bazel_rules_scala//scala/private:phases/phase_merge_jars.bzl", _phase_merge_jars = "phase_merge_jars") +load("@io_bazel_rules_scala//scala/private:phases/phase_link_scalajs.bzl", _phase_link_scalajs = "phase_link_scalajs") load("@io_bazel_rules_scala//scala/private:phases/phase_jvm_flags.bzl", _phase_jvm_flags = "phase_jvm_flags") load("@io_bazel_rules_scala//scala/private:phases/phase_coverage_runfiles.bzl", _phase_coverage_runfiles = "phase_coverage_runfiles") load("@io_bazel_rules_scala//scala/private:phases/phase_scalafmt.bzl", _phase_scalafmt = "phase_scalafmt") @@ -91,6 +93,9 @@ phase_declare_executable = _phase_declare_executable # merge_jars phase_merge_jars = _phase_merge_jars +# link scala.js +phase_link_scalajs = _phase_link_scalajs + # jvm_flags phase_jvm_flags = _phase_jvm_flags @@ -121,6 +126,7 @@ phase_collect_jars_common = _phase_collect_jars_common # compile phase_compile_binary = _phase_compile_binary phase_compile_library = _phase_compile_library +phase_compile_scalajs_library = _phase_compile_scalajs_library phase_compile_library_for_plugin_bootstrapping = _phase_compile_library_for_plugin_bootstrapping phase_compile_macro_library = _phase_compile_macro_library phase_compile_junit_test = _phase_compile_junit_test diff --git a/scala/private/rules/scala_binary.bzl b/scala/private/rules/scala_binary.bzl index bfe6fa249..a7927c5b1 100644 --- a/scala/private/rules/scala_binary.bzl +++ b/scala/private/rules/scala_binary.bzl @@ -20,6 +20,7 @@ load( "phase_dependency_common", "phase_java_wrapper_common", "phase_merge_jars", + "phase_link_scalajs", "phase_runfiles_common", "phase_scalac_provider", "phase_write_executable_common", @@ -80,3 +81,61 @@ def make_scala_binary(*extras): ) scala_binary = make_scala_binary() + +def _scalajs_binary_impl(ctx): + return run_phases( + ctx, + # customizable phases + [ + ("scalac_provider", phase_scalac_provider), + #("write_manifest", phase_write_manifest), + ("dependency", phase_dependency_common), + ("collect_jars", phase_collect_jars_common), + #("java_wrapper", phase_java_wrapper_common), + #("declare_executable", phase_declare_executable), + # no need to build an ijar for an executable + #("compile", phase_compile_binary), + #("coverage", phase_coverage_common), + ("link_scalajs", phase_link_scalajs), + #("runfiles", phase_runfiles_common), + #("write_executable", phase_write_executable_common), + ("default_info", phase_default_info), + ], + ) + +_scalajs_binary_attrs = { + "main_class": attr.string(mandatory = True), + "method": attr.string(mandatory = True), + "classpath_resources": attr.label_list(allow_files = True), + "_scalajs_linker": attr.label( + default = Label("@io_bazel_rules_scala//src/scala/scripts:scalajs_linker") + ), +} + +_scalajs_binary_attrs.update(implicit_deps) + +_scalajs_binary_attrs.update(common_attrs) + +_scalajs_binary_attrs.update(resolve_deps) + +_scalajs_binary_attrs.update() + +def make_scalajs_binary(*extras): + return rule( + attrs = _dicts.add( + _scalajs_binary_attrs, + extras_phases(extras), + *[extra["attrs"] for extra in extras if "attrs" in extra] + ), + executable = False, + # TODO: what does fragments do? + fragments = ["java"], + outputs = _dicts.add( + {"js": "%{name}.js"}, + *[extra["outputs"] for extra in extras if "outputs" in extra] + ), + toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scalajs_binary_impl, + ) + +scalajs_binary = make_scalajs_binary() diff --git a/scala/private/rules/scala_library.bzl b/scala/private/rules/scala_library.bzl index 23805df49..d26016a7f 100644 --- a/scala/private/rules/scala_library.bzl +++ b/scala/private/rules/scala_library.bzl @@ -26,6 +26,7 @@ load( "phase_compile_library", "phase_compile_library_for_plugin_bootstrapping", "phase_compile_macro_library", + "phase_compile_scalajs_library", "phase_coverage_common", "phase_coverage_library", "phase_default_info", @@ -251,3 +252,55 @@ def make_scala_macro_library(*extras): ) scala_macro_library = make_scala_macro_library() + + +## +# scalajs_library +## + +def _scalajs_library_impl(ctx): + return run_phases( + ctx, + # customizable phases + [ + ("scalac_provider", phase_scalac_provider), + ("collect_srcjars", phase_collect_srcjars), + ("write_manifest", phase_write_manifest), + ("dependency", phase_dependency_common), + ("collect_jars", phase_collect_jars_common), + ("compile", phase_compile_scalajs_library), + ("coverage", phase_coverage_library), + ("merge_jars", phase_merge_jars), + ("runfiles", phase_runfiles_library), + ("collect_exports_jars", phase_collect_exports_jars), + ("default_info", phase_default_info), + ], + ) + +_scalajs_library_attrs = {} + +_scalajs_library_attrs.update(implicit_deps) + +_scalajs_library_attrs.update(common_attrs) + +_scalajs_library_attrs.update(_library_attrs) + +_scalajs_library_attrs.update(resolve_deps) + +def make_scalajs_library(*extras): + return rule( + attrs = _dicts.add( + _scalajs_library_attrs, + extras_phases(extras), + *[extra["attrs"] for extra in extras if "attrs" in extra] + ), + fragments = ["java"], + outputs = _dicts.add( + common_outputs, + *[extra["outputs"] for extra in extras if "outputs" in extra] + ), + toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scalajs_library_impl, + ) + +scalajs_library = make_scalajs_library() diff --git a/scala/scala.bzl b/scala/scala.bzl index fb4a9eeaf..15953ed35 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -9,6 +9,7 @@ load( load( "@io_bazel_rules_scala//scala/private:rules/scala_binary.bzl", _scala_binary = "scala_binary", + _scalajs_binary = "scalajs_binary", ) load( "@io_bazel_rules_scala//scala/private:rules/scala_doc.bzl", @@ -21,6 +22,7 @@ load( load( "@io_bazel_rules_scala//scala/private:rules/scala_library.bzl", _scala_library = "scala_library", + _scalajs_library = "scalajs_library", _scala_library_for_plugin_bootstrapping = "scala_library_for_plugin_bootstrapping", _scala_library_suite = "scala_library_suite", _scala_macro_library = "scala_macro_library", @@ -50,9 +52,11 @@ def scala_specs2_junit_test(name, **kwargs): # Re-export private rules for public consumption scala_binary = _scala_binary +scalajs_binary = _scalajs_binary scala_doc = _scala_doc scala_junit_test = _scala_junit_test scala_library = _scala_library +scalajs_library = _scalajs_library scala_library_for_plugin_bootstrapping = _scala_library_for_plugin_bootstrapping scala_library_suite = _scala_library_suite scala_macro_library = _scala_macro_library diff --git a/scala/scala_toolchain.bzl b/scala/scala_toolchain.bzl index 5a705e21d..905a22d88 100644 --- a/scala/scala_toolchain.bzl +++ b/scala/scala_toolchain.bzl @@ -2,6 +2,7 @@ load( "@io_bazel_rules_scala//scala:providers.bzl", _DepsInfo = "DepsInfo", ) +load("//scala/private/toolchain_deps:toolchain_deps.bzl", "expose_toolchain_deps") def _compute_strict_deps_mode(input_strict_deps_mode, dependency_mode): if dependency_mode == "direct": @@ -104,3 +105,41 @@ scala_toolchain = rule( }, fragments = ["java"], ) + +def _scala_deps_toolchain(ctx): + toolchain = platform_common.ToolchainInfo( + dep_providers = ctx.attr.dep_providers, + ) + return [toolchain] + +scala_deps_toolchain = rule( + _scala_deps_toolchain, + attrs = { + "dep_providers": attr.label_list( + default = [ + "@io_bazel_rules_scala//scala:scala_compile_classpath_provider", + "@io_bazel_rules_scala//scala:scala_library_classpath_provider", + "@io_bazel_rules_scala//scala:scala_macro_classpath_provider", + "@io_bazel_rules_scala//scala:scala_xml_provider", + "@io_bazel_rules_scala//scala:parser_combinators_provider", + "@io_bazel_rules_scala//scala:scalajs_linker_provider", + ], + cfg = "target", + providers = [_DepsInfo], + ), + }, +) + +def _export_scala_toolchain_deps(ctx): + return expose_toolchain_deps(ctx, "@io_bazel_rules_scala//scala:deps_toolchain_type") + +export_scala_toolchain_deps = rule( + _export_scala_toolchain_deps, + attrs = { + "deps_id": attr.string( + mandatory = True, + ), + }, + #incompatible_use_toolchain_transition = True, + toolchains = ["@io_bazel_rules_scala//scala:deps_toolchain_type"], +) diff --git a/scala/toolchains.bzl b/scala/toolchains.bzl index b707c96cd..9ceee7a34 100644 --- a/scala/toolchains.bzl +++ b/scala/toolchains.bzl @@ -3,7 +3,15 @@ def scala_register_toolchains(): "@io_bazel_rules_scala//scala:default_toolchain", ) + native.register_toolchains( + "@io_bazel_rules_scala//scala:default_deps_toolchain", + ) + def scala_register_unused_deps_toolchains(): native.register_toolchains( "@io_bazel_rules_scala//scala:unused_dependency_checker_error_toolchain", ) + + native.register_toolchains( + "@io_bazel_rules_scala//scala:default_deps_toolchain", + ) diff --git a/scala_proto/private/scala_proto_default_repositories.bzl b/scala_proto/private/scala_proto_default_repositories.bzl index 17404a3aa..82494edfe 100644 --- a/scala_proto/private/scala_proto_default_repositories.bzl +++ b/scala_proto/private/scala_proto_default_repositories.bzl @@ -199,4 +199,4 @@ def scala_proto_default_repositories( actual = "@scala_proto_rules_opencensus_contrib_grpc_metrics//jar", ) - native.register_toolchains("@io_bazel_rules_scala//scala_proto:default_deps_toolchain") +# native.register_toolchains("@io_bazel_rules_scala//scala_proto:default_deps_toolchain") diff --git a/scala_proto/private/scalapb_aspect.bzl b/scala_proto/private/scalapb_aspect.bzl index eabea5da2..09c5d3eb4 100644 --- a/scala_proto/private/scalapb_aspect.bzl +++ b/scala_proto/private/scalapb_aspect.bzl @@ -21,6 +21,9 @@ ScalaPBInfo = provider(fields = [ "aspect_info", ]) +TRUE = "True" +FALSE = "False" + def merge_proto_infos(tis): return struct( transitive_sources = [t.transitive_sources for t in tis], @@ -52,6 +55,7 @@ def _compile_scala( scalapb_jar, deps_java_info, implicit_deps, + plugins, resources, resource_strip_prefix): manifest = ctx.actions.declare_file( @@ -84,7 +88,7 @@ def _compile_scala( cjars = merged_deps.compile_jars, all_srcjars = depset([scalapb_jar]), transitive_compile_jars = merged_deps.transitive_compile_time_jars, - plugins = [], + plugins = plugins, resource_strip_prefix = resource_strip_prefix, resources = resources, resource_jars = [], @@ -145,18 +149,45 @@ def _scalapb_aspect_impl(target, ctx): toolchain = ctx.toolchains[toolchain_type_label] flags = [] + compile_deps_id = "scalapb_compile_deps" if ctx.attr.scalajs == FALSE else "scalajs_proto_compile_deps" + compile_deps = find_deps_info_on( ctx, deps_toolchain_type_label, - "scalapb_compile_deps", + compile_deps_id, ).deps imps = [dep[JavaInfo] for dep in compile_deps] + plugins = [] + if ctx.attr.scalajs == TRUE: + # TODO: enforce this only has a single jar? + scalajs_compiler_plugin = find_deps_info_on( + ctx, + deps_toolchain_type_label, + "scalajs_compiler_plugin", + ).deps + imps.extend([dep[JavaInfo] for dep in scalajs_compiler_plugin]) + plugins.extend(scalajs_compiler_plugin) + + # TODO: cleanup, maybe switch from a named gen in dict to full grpcweb_generator attr? + named_generators = toolchain.named_generators if toolchain.with_grpc: - grpc_deps = find_deps_info_on(ctx, deps_toolchain_type_label, "scalapb_grpc_deps").deps - flags.append("grpc") - imps.extend([dep[JavaInfo] for dep in grpc_deps]) + if ctx.attr.scalajs == FALSE: + grpc_deps = find_deps_info_on(ctx, deps_toolchain_type_label, "scalapb_grpc_deps").deps + imps.extend([dep[JavaInfo] for dep in grpc_deps]) + flags.append("grpc") + + # blacklist grpc-web generator from non scala.js rules + named_generators = { + key: value + for key, value in named_generators.items() + if key != "grpc-web" + } + else: + grpc_deps = find_deps_info_on(ctx, deps_toolchain_type_label, "scalajs_grpc_deps").deps + imps.extend([dep[JavaInfo] for dep in grpc_deps]) + pass if toolchain.with_flat_package: flags.append("flat_package") @@ -197,7 +228,7 @@ def _scalapb_aspect_impl(target, ctx): target_ti.transitive_proto_path.to_list(), flags, scalapb_file, - toolchain.named_generators, + named_generators, sorted(extra_generator_jars), ) @@ -212,6 +243,7 @@ def _scalapb_aspect_impl(target, ctx): scalapb_file, deps, imps, + plugins, compile_protos, "" if target_ti.proto_source_root == "." else target_ti.proto_source_root, ) @@ -249,8 +281,9 @@ scalapb_aspect = aspect( cfg = "exec", default = "@com_google_protobuf//:protoc", ), + "scalajs": attr.string(values = [TRUE, FALSE]), }, - incompatible_use_toolchain_transition = True, + #incompatible_use_toolchain_transition = True, toolchains = [ "@io_bazel_rules_scala//scala:toolchain_type", "@io_bazel_rules_scala//scala_proto:toolchain_type", diff --git a/scala_proto/scala_proto.bzl b/scala_proto/scala_proto.bzl index 2dd296f86..6522828df 100644 --- a/scala_proto/scala_proto.bzl +++ b/scala_proto/scala_proto.bzl @@ -13,8 +13,10 @@ load( ) load( "//scala_proto/private:scalapb_aspect.bzl", + "FALSE", "ScalaPBAspectInfo", "ScalaPBInfo", + "TRUE", "merge_scalapb_aspect_info", "scalapb_aspect", ) @@ -55,9 +57,13 @@ scala_proto_library = rule( implementation = _scala_proto_library_impl, attrs = { "deps": attr.label_list(aspects = [scalapb_aspect]), + "scalajs": attr.string(default = FALSE), }, provides = [DefaultInfo, ScalaPBInfo, JavaInfo], ) def scalapb_proto_library(**kwargs): scala_proto_library(**kwargs) + +def scalajs_proto_library(**kwargs): + scala_proto_library(scalajs = TRUE, **kwargs) diff --git a/scala_proto/scala_proto_toolchain.bzl b/scala_proto/scala_proto_toolchain.bzl index 4a12f9ca9..c4b866c71 100644 --- a/scala_proto/scala_proto_toolchain.bzl +++ b/scala_proto/scala_proto_toolchain.bzl @@ -77,6 +77,6 @@ export_scalapb_toolchain_deps = rule( mandatory = True, ), }, - incompatible_use_toolchain_transition = True, + #incompatible_use_toolchain_transition = True, toolchains = ["@io_bazel_rules_scala//scala_proto:deps_toolchain_type"], ) diff --git a/src/scala/scripts/BUILD b/src/scala/scripts/BUILD index 1df47fba8..da08cf932 100644 --- a/src/scala/scripts/BUILD +++ b/src/scala/scripts/BUILD @@ -49,3 +49,25 @@ scala_binary( ":scalapb_worker_lib", ], ) + +scala_library( + name = "scalajs_linker_lib", + srcs = [ + "ScalaJSLinkRequest.scala", + "ScalaJSLinker.scala", + ], + visibility = ["//visibility:public"], + deps = [ + "//scala:scalajs_linker_deps", + "//src/java/io/bazel/rulesscala/worker", + ], +) + +scala_binary( + name = "scalajs_linker", + main_class = "scripts.ScalaJSLinker", + visibility = ["//visibility:public"], + deps = [ + ":scalajs_linker_lib", + ], +) diff --git a/src/scala/scripts/ScalaJSLinkRequest.scala b/src/scala/scripts/ScalaJSLinkRequest.scala new file mode 100644 index 000000000..cb9da65fe --- /dev/null +++ b/src/scala/scripts/ScalaJSLinkRequest.scala @@ -0,0 +1,29 @@ +package scripts + +import org.scalajs.linker.interface.{ModuleKind, StandardConfig} + +case class ScalaJSLinkRequest( + jsOutput: String, + mainClass: String, + mainMethod: String, + classPath: Seq[String], + config: StandardConfig +) + +object ScalaJSLinkRequest { + def from(args: Array[String]): ScalaJSLinkRequest = { + // TODO: determine what linker configuration values we'd like to support. + ScalaJSLinkRequest( + args(0), + args(1), + args(2), + args(3).split(":").toSeq, + config = StandardConfig() + .withOptimizer(true) + .withParallel(true) + .withSourceMap(false) + .withClosureCompilerIfAvailable(true) + .withModuleKind(ModuleKind.CommonJSModule) + ) + } +} diff --git a/src/scala/scripts/ScalaJSLinker.scala b/src/scala/scripts/ScalaJSLinker.scala new file mode 100644 index 000000000..f3f213a0b --- /dev/null +++ b/src/scala/scripts/ScalaJSLinker.scala @@ -0,0 +1,52 @@ +package scripts + +import java.nio.file.Paths +import java.nio.file.Files + +import org.scalajs.linker.interface.{ModuleInitializer, StandardConfig} +import org.scalajs.linker.{MemOutputDirectory, PathIRContainer, StandardImpl} +import org.scalajs.logging.{Level, ScalaConsoleLogger} + +import scala.concurrent.Await +import scala.concurrent.duration.Duration +import scala.concurrent.ExecutionContext.Implicits.global +import io.bazel.rulesscala.worker.Worker + +object ScalaJSLinker extends Worker.Interface { + def main(args: Array[String]): Unit = Worker.workerMain(args, ScalaJSLinker) + + override def work(args: Array[String]): Unit = { + val linkRequest = ScalaJSLinkRequest.from(args) + + val linkerConfig = linkRequest.config + val inputFiles = linkRequest.classPath.map(path => Paths.get(".", path)) + + val linker = StandardImpl.linker(linkerConfig) + val moduleInitializers = Seq( + ModuleInitializer.mainMethod(linkRequest.mainClass, linkRequest.mainMethod) + ) + val outputDirectory = MemOutputDirectory() + val cache = StandardImpl.irFileCache().newCache + + val result = PathIRContainer + .fromClasspath(inputFiles) + .map(_._1) + .flatMap(cache.cached _) + .flatMap( + linker.link(_, moduleInitializers, outputDirectory, new ScalaConsoleLogger(Level.Error)) + ) + + val report = Await.result(result, Duration.Inf) + + if (report.publicModules.size != 1) + throw new AssertionError(s"got other than 1 module: $report") + + val jsOutput = outputDirectory + .content(report.publicModules.head.jsFileName) + .getOrElse( + throw new RuntimeException("compiled js not found") + ) + + Files.write(Paths.get(".", linkRequest.jsOutput), jsOutput) + } +} diff --git a/third_party/repositories/scala_2_11.bzl b/third_party/repositories/scala_2_11.bzl index 9eaf1feaf..2283bf78e 100644 --- a/third_party/repositories/scala_2_11.bzl +++ b/third_party/repositories/scala_2_11.bzl @@ -27,6 +27,14 @@ artifacts = { "artifact": "org.scala-lang.modules:scala-parser-combinators_2.11:1.0.4", "sha256": "0dfaafce29a9a245b0a9180ec2c1073d2bd8f0330f03a9f1f6a74d1bc83f62d6", }, + "io_bazel_rules_scala_scalajs_linker": { + "artifact": "org.scala-js:scalajs-linker_2.11:1.5.0", + "sha256": "", + }, + "io_bazel_rules_scala_scalajs_ir": { + "artifact": "org.scala-js:scalajs-ir_2.11:1.5.0", + "sha256": "", + }, "org_scalameta_common": { "artifact": "org.scalameta:common_2.11:4.3.0", "sha256": "6330798bcbd78d14d371202749f32efda0465c3be5fd057a6055a67e21335ba0", diff --git a/third_party/repositories/scala_2_12.bzl b/third_party/repositories/scala_2_12.bzl index e5319118d..7b08e0d0e 100644 --- a/third_party/repositories/scala_2_12.bzl +++ b/third_party/repositories/scala_2_12.bzl @@ -27,6 +27,14 @@ artifacts = { "artifact": "org.scala-lang.modules:scala-parser-combinators_2.12:1.0.4", "sha256": "282c78d064d3e8f09b3663190d9494b85e0bb7d96b0da05994fe994384d96111", }, + "io_bazel_rules_scala_scalajs_linker": { + "artifact": "org.scala-js:scalajs-linker_2.12:1.5.0", + "sha256": "", + }, + "io_bazel_rules_scala_scalajs_ir": { + "artifact": "org.scala-js:scalajs-ir_2.12:1.5.0", + "sha256": "", + }, "org_scalameta_common": { "artifact": "org.scalameta:common_2.12:4.3.0", "sha256": "3bdb2ff71d3e86f94b4d31d2c40442f533655860749a92fd17e1f29b8deb8baa",