diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl index 9436e2d0a..bc11e2e7d 100644 --- a/scala/private/rule_impls.bzl +++ b/scala/private/rule_impls.bzl @@ -590,74 +590,97 @@ def _jar_path_based_on_java_bin(ctx): jar_path = java_bin.rpartition("/")[0] + "/jar" return jar_path -def _write_executable(ctx, rjars, main_class, jvm_flags, wrapper, use_jacoco): - template = ctx.attr._java_stub_template.files.to_list()[0] - - jvm_flags = " ".join( - [ctx.expand_location(f, ctx.attr.data) for f in jvm_flags], - ) - - javabin = "export REAL_EXTERNAL_JAVA_BIN=${JAVABIN};JAVABIN=%s/%s" % ( - _runfiles_root(ctx), - wrapper.short_path, - ) - - if use_jacoco and _coverage_replacements_provider.is_enabled(ctx): - classpath = ":".join( - ["${RUNPATH}%s" % (j.short_path) for j in rjars.to_list() + ctx.files._jacocorunner + ctx.files._lcov_merger], - ) - jacoco_metadata_file = ctx.actions.declare_file( - "%s.jacoco_metadata.txt" % ctx.attr.name, - sibling = ctx.outputs.executable, +def _write_executable(ctx, executable, rjars, main_class, jvm_flags, wrapper, use_jacoco): + if (_is_windows(ctx)): + classpath = ";".join( + [("external/%s" % (j.short_path[3:]) if j.short_path.startswith("../") else j.short_path) for j in rjars.to_list()], ) - ctx.actions.write(jacoco_metadata_file, "\n".join([ - jar.short_path.replace("../", "external/") - for jar in rjars - ])) - ctx.actions.expand_template( - template = template, - output = ctx.outputs.executable, - substitutions = { - "%classpath%": classpath, - "%javabin%": javabin, - "%jarbin%": _jar_path_based_on_java_bin(ctx), - "%jvm_flags%": jvm_flags, - "%needs_runfiles%": "", - "%runfiles_manifest_only%": "", - "%workspace_prefix%": ctx.workspace_name + "/", - "%java_start_class%": "com.google.testing.coverage.JacocoCoverageRunner", - "%set_jacoco_metadata%": "export JACOCO_METADATA_JAR=\"$JAVA_RUNFILES/{}/{}\"".format(ctx.workspace_name, jacoco_metadata_file.short_path), - "%set_jacoco_main_class%": """export JACOCO_MAIN_CLASS={}""".format(main_class), - "%set_jacoco_java_runfiles_root%": """export JACOCO_JAVA_RUNFILES_ROOT=$JAVA_RUNFILES/{}/""".format(ctx.workspace_name), - }, - is_executable = True, + jvm_flags_str = ";".join(jvm_flags) + java_for_exe = "%s/%s" % (ctx.workspace_name, str(ctx.attr._java_runtime[java_common.JavaRuntimeInfo].java_executable_exec_path)) + + ctx.actions.run( + outputs = [executable], + inputs = [], + executable = ctx.attr._exe.files_to_run.executable, + arguments = [executable.path, ctx.workspace_name, java_for_exe, main_class, classpath, jvm_flags_str], + mnemonic = "ExeLauncher", + progress_message = "Creating exe launcher", ) - return [jacoco_metadata_file] + return [] else: - # RUNPATH is defined here: - # https://github.com/bazelbuild/bazel/blob/0.4.5/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt#L227 - classpath = ":".join( - ["${RUNPATH}%s" % (j.short_path) for j in rjars.to_list()], + template = ctx.attr._java_stub_template.files.to_list()[0] + + jvm_flags = " ".join( + [ctx.expand_location(f, ctx.attr.data) for f in jvm_flags], ) - ctx.actions.expand_template( - template = template, - output = ctx.outputs.executable, - substitutions = { - "%classpath%": classpath, - "%java_start_class%": main_class, - "%javabin%": javabin, - "%jarbin%": _jar_path_based_on_java_bin(ctx), - "%jvm_flags%": jvm_flags, - "%needs_runfiles%": "", - "%runfiles_manifest_only%": "", - "%set_jacoco_metadata%": "", - "%set_jacoco_main_class%": "", - "%set_jacoco_java_runfiles_root%": "", - "%workspace_prefix%": ctx.workspace_name + "/", - }, - is_executable = True, + + javabin = "export REAL_EXTERNAL_JAVA_BIN=${JAVABIN};JAVABIN=%s/%s" % ( + _runfiles_root(ctx), + wrapper.short_path, ) - return [] + + if use_jacoco and _coverage_replacements_provider.is_enabled(ctx): + classpath = ":".join( + ["${RUNPATH}%s" % (j.short_path) for j in rjars.to_list() + ctx.files._jacocorunner + ctx.files._lcov_merger], + ) + jacoco_metadata_file = ctx.actions.declare_file( + "%s.jacoco_metadata.txt" % ctx.attr.name, + sibling = executable, + ) + ctx.actions.write(jacoco_metadata_file, "\n".join([ + jar.short_path.replace("../", "external/") + for jar in rjars + ])) + ctx.actions.expand_template( + template = template, + output = executable, + substitutions = { + "%classpath%": classpath, + "%javabin%": javabin, + "%jarbin%": _jar_path_based_on_java_bin(ctx), + "%jvm_flags%": jvm_flags, + "%needs_runfiles%": "", + "%runfiles_manifest_only%": "", + "%workspace_prefix%": ctx.workspace_name + "/", + "%java_start_class%": "com.google.testing.coverage.JacocoCoverageRunner", + "%set_jacoco_metadata%": "export JACOCO_METADATA_JAR=\"$JAVA_RUNFILES/{}/{}\"".format(ctx.workspace_name, jacoco_metadata_file.short_path), + "%set_jacoco_main_class%": """export JACOCO_MAIN_CLASS={}""".format(main_class), + "%set_jacoco_java_runfiles_root%": """export JACOCO_JAVA_RUNFILES_ROOT=$JAVA_RUNFILES/{}/""".format(ctx.workspace_name), + }, + is_executable = True, + ) + return [jacoco_metadata_file] + else: + # RUNPATH is defined here: + # https://github.com/bazelbuild/bazel/blob/0.4.5/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt#L227 + classpath = ":".join( + ["${RUNPATH}%s" % (j.short_path) for j in rjars.to_list()], + ) + ctx.actions.expand_template( + template = template, + output = executable, + substitutions = { + "%classpath%": classpath, + "%java_start_class%": main_class, + "%javabin%": javabin, + "%jarbin%": _jar_path_based_on_java_bin(ctx), + "%jvm_flags%": jvm_flags, + "%needs_runfiles%": "", + "%runfiles_manifest_only%": "", + "%set_jacoco_metadata%": "", + "%set_jacoco_main_class%": "", + "%set_jacoco_java_runfiles_root%": "", + "%workspace_prefix%": ctx.workspace_name + "/", + }, + is_executable = True, + ) + return [] + +def _declare_executable(ctx): + if (_is_windows(ctx)): + return ctx.actions.declare_file("%s.exe" % ctx.label.name) + else: + return ctx.actions.declare_file(ctx.label.name) def _collect_runtime_jars(dep_targets): runtime_jars = [] @@ -848,6 +871,7 @@ def scala_macro_library_impl(ctx): # Common code shared by all scala binary implementations. def _scala_binary_common( ctx, + executable, cjars, rjars, transitive_compile_time_jars, @@ -876,7 +900,7 @@ def _scala_binary_common( runfiles = ctx.runfiles( transitive_files = depset( - [ctx.outputs.executable, java_wrapper] + ctx.files._java_runtime, + [executable, java_wrapper] + ctx.files._java_runtime, transitive = [rjars], ), collect_data = True, @@ -898,8 +922,9 @@ def _scala_binary_common( java_provider = create_java_provider(scalaattr, transitive_compile_time_jars) return struct( + executable = executable, coverage = outputs.coverage, - files = depset([ctx.outputs.executable, ctx.outputs.jar]), + files = depset([executable, ctx.outputs.jar]), instrumented_files = outputs.coverage.instrumented_files, providers = [java_provider, jars2labels] + outputs.coverage.providers, runfiles = runfiles, @@ -950,8 +975,12 @@ def scala_binary_impl(ctx): (cjars, transitive_rjars) = (jars.compile_jars, jars.transitive_runtime_jars) wrapper = _write_java_wrapper(ctx, "", "") + + executable = _declare_executable(ctx) + out = _scala_binary_common( ctx, + executable, cjars, transitive_rjars, jars.transitive_compile_jars, @@ -966,6 +995,7 @@ def scala_binary_impl(ctx): ) _write_executable( ctx = ctx, + executable = executable, jvm_flags = ctx.attr.jvm_flags, main_class = ctx.attr.main_class, rjars = out.transitive_rjars, @@ -989,6 +1019,9 @@ def scala_repl_impl(ctx): (cjars, transitive_rjars) = (jars.compile_jars, jars.transitive_runtime_jars) args = " ".join(ctx.attr.scalacopts) + + executable = _declare_executable(ctx) + wrapper = _write_java_wrapper( ctx, args, @@ -1010,6 +1043,7 @@ trap finish EXIT out = _scala_binary_common( ctx, + executable, cjars, transitive_rjars, jars.transitive_compile_jars, @@ -1024,6 +1058,7 @@ trap finish EXIT ) _write_executable( ctx = ctx, + executable = executable, jvm_flags = ["-Dscala.usejavacp=true"] + ctx.attr.jvm_flags, main_class = "scala.tools.nsc.MainGenericRunner", rjars = out.transitive_rjars, @@ -1086,10 +1121,13 @@ def scala_test_impl(ctx): "-C io.bazel.rules.scala.JUnitXmlReporter ", ]) + executable = _declare_executable(ctx) + # main_class almost has to be "org.scalatest.tools.Runner" due to args.... wrapper = _write_java_wrapper(ctx, args, "") out = _scala_binary_common( ctx, + executable, cjars, transitive_rjars, transitive_compile_jars, @@ -1117,6 +1155,7 @@ def scala_test_impl(ctx): coverage_runfiles.extend(_write_executable( ctx = ctx, + executable = executable, jvm_flags = ctx.attr.jvm_flags, main_class = ctx.attr.main_class, rjars = rjars, @@ -1125,6 +1164,7 @@ def scala_test_impl(ctx): )) return struct( + executable = executable, files = out.files, instrumented_files = out.instrumented_files, providers = out.providers, @@ -1202,9 +1242,12 @@ def scala_junit_test_impl(ctx): ctx.attr._hamcrest, ] + executable = _declare_executable(ctx) + wrapper = _write_java_wrapper(ctx, "", "") out = _scala_binary_common( ctx, + executable, cjars, transitive_rjars, jars.transitive_compile_jars, @@ -1237,6 +1280,7 @@ def scala_junit_test_impl(ctx): ] _write_executable( ctx = ctx, + executable = executable, jvm_flags = launcherJvmFlags + ctx.attr.jvm_flags, main_class = "com.google.testing.junit.runner.BazelTestRunner", rjars = out.transitive_rjars, @@ -1293,3 +1337,6 @@ def _jacoco_offline_instrument(ctx, input_jar): def _jacoco_offline_instrument_format_each(in_out_pair): return (["%s=%s" % (in_out_pair[0].path, in_out_pair[1].path)]) + +def _is_windows(ctx): + return ctx.configuration.host_path_separator == ";" diff --git a/scala/scala.bzl b/scala/scala.bzl index d201f3015..98761c594 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -67,6 +67,11 @@ _implicit_deps = { "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/scalac", ), ), + "_exe": attr.label( + executable = True, + cfg = "host", + default = Label("@io_bazel_rules_scala//src/java/io/bazel/rulesscala/exe:exe"), + ), } # Single dep to allow IDEs to pickup all the implicit dependencies. @@ -472,6 +477,14 @@ def scala_repositories( server_urls = maven_servers, ) + _scala_maven_import_external( + name = "io_bazel_rules_scala_guava", + artifact = "com.google.guava:guava:21.0", + jar_sha256 = "972139718abc8a4893fa78cba8cf7b2c903f35c97aaf44fa3031b0669948b480", + licenses = ["notice"], + server_urls = maven_servers, + ) + _scala_maven_import_external( name = "io_bazel_rules_scala_org_jacoco_org_jacoco_core", artifact = "org.jacoco:org.jacoco.core:0.7.5.201505241946", @@ -487,7 +500,7 @@ def scala_repositories( licenses = ["notice"], server_urls = maven_servers, ) - + # Using this and not the bazel regular one due to issue when classpath is too long # until https://github.com/bazelbuild/bazel/issues/6955 is resolved if native.existing_rule("java_stub_template") == None: @@ -538,6 +551,11 @@ def scala_repositories( actual = "@io_bazel_rules_scala_scala_parser_combinators", ) + native.bind( + name = "io_bazel_rules_scala/dependency/scala/guava", + actual = "@io_bazel_rules_scala_guava", + ) + def _sanitize_string_for_usage(s): res_array = [] for idx in range(len(s)): diff --git a/src/java/io/bazel/rulesscala/exe/BUILD b/src/java/io/bazel/rulesscala/exe/BUILD new file mode 100644 index 000000000..3cea7c9a9 --- /dev/null +++ b/src/java/io/bazel/rulesscala/exe/BUILD @@ -0,0 +1,24 @@ +java_library( + name = "exe-lib", + srcs = [ + "LauncherFileWriter.java", + "LaunchInfo.java", + ], + deps = [ + "@bazel_tools//tools/java/runfiles:runfiles", + "//external:io_bazel_rules_scala/dependency/scala/guava", + ], + visibility = ["//visibility:public"], +) + +java_binary( + name = "exe", + main_class = "io.bazel.rulesscala.exe.LauncherFileWriter", + visibility = ["//visibility:public"], + runtime_deps = [ + ":exe-lib", + ], + data = [ + "@bazel_tools//tools/launcher:launcher", + ], +) diff --git a/src/java/io/bazel/rulesscala/exe/LaunchInfo.java b/src/java/io/bazel/rulesscala/exe/LaunchInfo.java new file mode 100644 index 000000000..8931a3a41 --- /dev/null +++ b/src/java/io/bazel/rulesscala/exe/LaunchInfo.java @@ -0,0 +1,170 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.bazel.rulesscala.exe; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + + +/** + * Metadata that describes the payload of the native launcher binary. + * + *
This object constructs the binary metadata lazily, to save memory.
+ */
+public final class LaunchInfo {
+
+ private final ImmutableList Examples:
+ *
+ * Examples:
+ *
+ *
+ *
+ */
+ public Builder addKeyValuePair(String key, String value) {
+ Preconditions.checkNotNull(key);
+ if (!key.isEmpty()) {
+ entries.add(new KeyValuePair(key, value));
+ }
+ return this;
+ }
+
+ /**
+ * Adds a key and list of lazily-joined values.
+ *
+ *
+ *
+ */
+ public Builder addJoinedValues(
+ String key, String delimiter, Iterable