From bab69b3bc9a46daff01aaeb74904427732947dd1 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Fri, 15 Feb 2019 18:12:41 -0800 Subject: [PATCH 01/11] Add preliminary support for code coverage --- .../coverage_replacements_provider.bzl | 85 +++++ scala/private/rule_impls.bzl | 312 ++++++++++++------ scala/scala.bzl | 160 +++++++-- .../rulesscala/coverage/instrumenter/BUILD | 29 ++ .../instrumenter/JacocoInstrumenter.java | 105 ++++++ test/coverage/A1.scala | 5 + test/coverage/A2.scala | 5 + test/coverage/B1.scala | 7 + test/coverage/B2.java | 9 + test/coverage/BUILD | 78 +++++ test/coverage/C2.scala | 5 + test/coverage/TestAll.scala | 12 + test/coverage/TestB2.java | 11 + test/coverage/expected-coverage.dat | 94 ++++++ test_rules_scala.sh | 9 +- tools/bazel | 111 +++++++ 16 files changed, 912 insertions(+), 125 deletions(-) create mode 100644 scala/private/coverage_replacements_provider.bzl create mode 100644 src/java/io/bazel/rulesscala/coverage/instrumenter/BUILD create mode 100644 src/java/io/bazel/rulesscala/coverage/instrumenter/JacocoInstrumenter.java create mode 100644 test/coverage/A1.scala create mode 100644 test/coverage/A2.scala create mode 100644 test/coverage/B1.scala create mode 100644 test/coverage/B2.java create mode 100644 test/coverage/BUILD create mode 100644 test/coverage/C2.scala create mode 100644 test/coverage/TestAll.scala create mode 100644 test/coverage/TestB2.java create mode 100755 test/coverage/expected-coverage.dat create mode 100755 tools/bazel diff --git a/scala/private/coverage_replacements_provider.bzl b/scala/private/coverage_replacements_provider.bzl new file mode 100644 index 000000000..0969a3398 --- /dev/null +++ b/scala/private/coverage_replacements_provider.bzl @@ -0,0 +1,85 @@ +# +# Coverage Replacements are a mapping of normal compiled artifacts to +# instrumented compiled artifacts. +# +# The intention is that the final test runner inspects the test +# classpath and replaces artifacts by any mappings found in the +# `replacements` field. +# +# Rules producing replacement artifacts should _not_ link the +# replacement files as any of the default outputs via DefaultInfo, +# JavaInfo, etc. This way, actions producing the replacement artifacts +# will be executed on an as needed basis. +# +# Coverage Replacements use a provider and a helper aspect to +# aggregate replacements files across the dependency graph. +# +# Under the hood, two providers are needed because Bazel doesn't allow +# duplicate providers. +# + +_CoverageReplacements = provider( + fields = { + "replacements": "hash of files to swap out", + }, +) + +_CombinedCoverageReplacements = provider( + fields = { + "replacements": "hash of files to swap out", + }, +) + +# the attributes used to form the dependency graph that we'll fold +# over for our aggregation +_dependency_attributes = [ + "deps", + "exports", +] + +def _combine(*entriess, base = {}): + return _CombinedCoverageReplacements(replacements = _dicts_add(base, *( + [ + entry[_CoverageReplacements].replacements + for entries in entriess + for entry in entries + if _CoverageReplacements in entry + ] + [ + entry[_CombinedCoverageReplacements].replacements + for entries in entriess + for entry in entries + if _CombinedCoverageReplacements in entry + ] + ))) + +def _from_ctx(ctx, base = {}): + return _combine( + base = base, + *[getattr(ctx.attr, name, []) for name in _dependency_attributes] + ) + +def _aspect_impl(target, ctx): + if JavaInfo not in target: + return [] + else: + return [_from_ctx(ctx.rule)] + +_aspect = aspect( + attr_aspects = _dependency_attributes, + implementation = _aspect_impl, +) + +coverage_replacements_provider = struct( + aspect = _aspect, + combine = _combine, + create = _CoverageReplacements, + dependency_attributes = _dependency_attributes, + from_ctx = _from_ctx, +) + +# from bazel's skylib +def _dicts_add(*dictionaries): + result = {} + for d in dictionaries: + result.update(d) + return result diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl index a5077817f..4ce3b3451 100644 --- a/scala/private/rule_impls.bzl +++ b/scala/private/rule_impls.bzl @@ -18,6 +18,10 @@ load( "create_scala_provider", _ScalacProvider = "ScalacProvider", ) +load( + "@io_bazel_rules_scala//scala/private:coverage_replacements_provider.bzl", + _coverage_replacements_provider = "coverage_replacements_provider", +) load( ":common.bzl", "add_labels_of_jars_to", @@ -30,9 +34,17 @@ load( load("@io_bazel_rules_scala//scala:jars_to_labels.bzl", "JarsToLabelsInfo") _java_extension = ".java" + _scala_extension = ".scala" + _srcjar_extension = ".srcjar" +_empty_coverage_struct = struct( + instrumented_files = struct(), + providers = [], + replacements = {}, +) + def _adjust_resources_path_by_strip_prefix(path, resource_strip_prefix): if not path.startswith(resource_strip_prefix): fail("Resource file %s is not under the specified prefix to strip" % path) @@ -221,7 +233,7 @@ CurrentTarget: {current_target} current_target = current_target, ) if is_dependency_analyzer_off(ctx) and not _is_plus_one_deps_off(ctx): - compiler_classpath_jars = transitive_compile_jars + compiler_classpath_jars = transitive_compile_jars plugins_list = plugins.to_list() plugin_arg = _join_path(plugins_list) @@ -375,9 +387,9 @@ def try_to_compile_java_jar( strict_deps = ctx.fragments.java.strict_java_deps, ) return struct( - jar = full_java_jar, ijar = provider.compile_jars.to_list().pop(), - source_jars = provider.source_jars + jar = full_java_jar, + source_jars = provider.source_jars, ) def collect_java_providers_of(deps): @@ -404,11 +416,12 @@ def _compile_or_empty( # no need to build ijar when empty return struct( - ijar = ctx.outputs.jar, class_jar = ctx.outputs.jar, - java_jar = False, + coverage = _empty_coverage_struct, full_jars = [ctx.outputs.jar], + ijar = ctx.outputs.jar, ijars = [ctx.outputs.jar], + java_jar = False, source_jars = [], ) else: @@ -455,9 +468,9 @@ def _compile_or_empty( ctx.attr.expect_java_output, ctx.attr.scalac_jvm_flags, ctx.attr._scalac, - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets, + unused_dependency_checker_mode = unused_dependency_checker_mode, ) # build ijar if needed @@ -489,12 +502,16 @@ def _compile_or_empty( full_jars += [java_jar.jar] ijars += [java_jar.ijar] source_jars += java_jar.source_jars + + coverage = _jacoco_offline_instrument(ctx, ctx.outputs.jar) + return struct( - ijar = ijar, class_jar = ctx.outputs.jar, - java_jar = java_jar, + coverage = coverage, full_jars = full_jars, + ijar = ijar, ijars = ijars, + java_jar = java_jar, source_jars = source_jars, ) @@ -537,8 +554,7 @@ def _write_java_wrapper(ctx, args = "", wrapper_preamble = ""): """This creates a wrapper that sets up the correct path to stand in for the java command.""" - java_path = str(ctx.attr._java_runtime[java_common.JavaRuntimeInfo] - .java_executable_runfiles_path) + java_path = str(ctx.attr._java_runtime[java_common.JavaRuntimeInfo].java_executable_runfiles_path) if _path_is_absolute(java_path): javabin = java_path else: @@ -567,38 +583,73 @@ JAVA_EXEC_TO_USE=${{REAL_EXTERNAL_JAVA_BIN:-$DEFAULT_JAVABIN}} ) return wrapper -def _write_executable(ctx, rjars, main_class, jvm_flags, wrapper): +def _write_executable(ctx, rjars, main_class, jvm_flags, wrapper, use_jacoco = False): template = ctx.attr._java_stub_template.files.to_list()[0] - # 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()], - ) 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%": "export REAL_EXTERNAL_JAVA_BIN=${JAVABIN};JAVABIN=%s/%s" % ( - _runfiles_root(ctx), - wrapper.short_path, - ), - "%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, ) + if use_jacoco: + 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, + ) + 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, + "%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 = ctx.outputs.executable, + substitutions = { + "%classpath%": classpath, + "%java_start_class%": main_class, + "%javabin%": javabin, + "%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 _collect_runtime_jars(dep_targets): runtime_jars = [] @@ -658,9 +709,9 @@ def _collect_jars_from_common_ctx( return struct( compile_jars = cjars, - transitive_runtime_jars = transitive_rjars, jars2labels = jars2labels, transitive_compile_jars = transitive_compile_jars, + transitive_runtime_jars = transitive_rjars, ) def _lib( @@ -694,12 +745,12 @@ def _lib( jars.transitive_compile_jars, jars.jars2labels.jars_to_labels, [], - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = [ target.label for target in base_classpath + ctx.attr.exports + unused_dependency_checker_ignored_targets ], + unused_dependency_checker_mode = unused_dependency_checker_mode, ) transitive_rjars = depset(outputs.full_jars, transitive = [transitive_rjars]) @@ -723,27 +774,28 @@ def _lib( source_jars = _pack_source_jars(ctx) + outputs.source_jars scalaattr = create_scala_provider( - ijar = outputs.ijar, class_jar = outputs.class_jar, compile_jars = depset( outputs.ijars, transitive = [exports_jars.compile_jars], ), - transitive_runtime_jars = transitive_rjars, deploy_jar = ctx.outputs.deploy_jar, full_jars = outputs.full_jars, - statsfile = ctx.outputs.statsfile, + ijar = outputs.ijar, source_jars = source_jars, + statsfile = ctx.outputs.statsfile, + transitive_runtime_jars = transitive_rjars, ) java_provider = create_java_provider(scalaattr, jars.transitive_compile_jars) return struct( files = depset([ctx.outputs.jar] + outputs.full_jars), # Here is the default output - scala = scalaattr, - providers = [java_provider, jars.jars2labels], - runfiles = runfiles, + instrumented_files = outputs.coverage.instrumented_files, jars_to_labels = jars.jars2labels, + providers = [java_provider, jars.jars2labels] + outputs.coverage.providers, + runfiles = runfiles, + scala = scalaattr, ) def get_unused_dependency_checker_mode(ctx): @@ -769,8 +821,8 @@ def scala_library_for_plugin_bootstrapping_impl(ctx): ctx, scalac_provider.default_classpath, True, - unused_dependency_checker_mode = "off", unused_dependency_checker_ignored_targets = [], + unused_dependency_checker_mode = "off", ) def scala_macro_library_impl(ctx): @@ -805,9 +857,9 @@ def _scala_binary_common( transitive_compile_time_jars, jars2labels.jars_to_labels, implicit_junit_deps_needed_for_java_compilation, - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets, + unused_dependency_checker_mode = unused_dependency_checker_mode, ) # no need to build an ijar for an executable rjars = depset(outputs.full_jars, transitive = [rjars]) @@ -824,53 +876,57 @@ def _scala_binary_common( source_jars = _pack_source_jars(ctx) + outputs.source_jars scalaattr = create_scala_provider( - ijar = outputs.class_jar, # we aren't using ijar here class_jar = outputs.class_jar, compile_jars = depset(outputs.ijars), - transitive_runtime_jars = rjars, deploy_jar = ctx.outputs.deploy_jar, full_jars = outputs.full_jars, - statsfile = ctx.outputs.statsfile, + ijar = outputs.class_jar, # we aren't using ijar here source_jars = source_jars, + statsfile = ctx.outputs.statsfile, + transitive_runtime_jars = rjars, ) java_provider = create_java_provider(scalaattr, transitive_compile_time_jars) return struct( + coverage = outputs.coverage, files = depset([ctx.outputs.executable, ctx.outputs.jar]), - providers = [java_provider, jars2labels], + instrumented_files = outputs.coverage.instrumented_files, + providers = [java_provider, jars2labels] + outputs.coverage.providers, + runfiles = runfiles, scala = scalaattr, transitive_rjars = rjars, #calling rules need this for the classpath in the launcher - runfiles = runfiles, ) def _pack_source_jars(ctx): - source_jars = [] - - # collect .scala sources and pack a source jar for Scala - scala_sources = [ - f for f in ctx.files.srcs - if f.basename.endswith(_scala_extension) - ] - - # collect .srcjar files and pack them with the scala sources - bundled_source_jars = [ - f for f in ctx.files.srcs - if f.basename.endswith(_srcjar_extension) - ] - scala_source_jar = java_common.pack_sources( - ctx.actions, - output_jar = ctx.outputs.jar, - sources = scala_sources, - source_jars = bundled_source_jars, - java_toolchain = ctx.attr._java_toolchain, - host_javabase = ctx.attr._host_javabase - ) - if scala_source_jar: - source_jars.append(scala_source_jar) - - return source_jars + source_jars = [] + + # collect .scala sources and pack a source jar for Scala + scala_sources = [ + f + for f in ctx.files.srcs + if f.basename.endswith(_scala_extension) + ] + + # collect .srcjar files and pack them with the scala sources + bundled_source_jars = [ + f + for f in ctx.files.srcs + if f.basename.endswith(_srcjar_extension) + ] + scala_source_jar = java_common.pack_sources( + ctx.actions, + output_jar = ctx.outputs.jar, + sources = scala_sources, + source_jars = bundled_source_jars, + java_toolchain = ctx.attr._java_toolchain, + host_javabase = ctx.attr._host_javabase, + ) + if scala_source_jar: + source_jars.append(scala_source_jar) + + return source_jars def scala_binary_impl(ctx): scalac_provider = _scalac_provider(ctx) @@ -892,18 +948,18 @@ def scala_binary_impl(ctx): jars.transitive_compile_jars, jars.jars2labels, wrapper, - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = [ target.label for target in scalac_provider.default_classpath + ctx.attr.unused_dependency_checker_ignored_targets ], + unused_dependency_checker_mode = unused_dependency_checker_mode, ) _write_executable( ctx = ctx, - rjars = out.transitive_rjars, - main_class = ctx.attr.main_class, jvm_flags = ctx.attr.jvm_flags, + main_class = ctx.attr.main_class, + rjars = out.transitive_rjars, wrapper = wrapper, ) return out @@ -949,18 +1005,18 @@ trap finish EXIT jars.transitive_compile_jars, jars.jars2labels, wrapper, - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = [ target.label for target in scalac_provider.default_repl_classpath + ctx.attr.unused_dependency_checker_ignored_targets ], + unused_dependency_checker_mode = unused_dependency_checker_mode, ) _write_executable( ctx = ctx, - rjars = out.transitive_rjars, - main_class = "scala.tools.nsc.MainGenericRunner", jvm_flags = ["-Dscala.usejavacp=true"] + ctx.attr.jvm_flags, + main_class = "scala.tools.nsc.MainGenericRunner", + rjars = out.transitive_rjars, wrapper = wrapper, ) @@ -1028,32 +1084,56 @@ def scala_test_impl(ctx): transitive_compile_jars, jars_to_labels, wrapper, - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets, + unused_dependency_checker_mode = unused_dependency_checker_mode, ) - _write_executable( + + rjars = out.transitive_rjars + + coverage_runfiles = [] + if ctx.configuration.coverage_enabled: + coverage_replacements = _coverage_replacements_provider.from_ctx( + ctx, + base = out.coverage.replacements, + ).replacements + + rjars = depset([ + coverage_replacements[jar] if jar in coverage_replacements else jar + for jar in rjars + ]) + coverage_runfiles = ctx.files._jacocorunner + ctx.files._lcov_merger + coverage_replacements.values() + + coverage_runfiles.extend(_write_executable( ctx = ctx, - rjars = out.transitive_rjars, - main_class = ctx.attr.main_class, jvm_flags = ctx.attr.jvm_flags, + main_class = ctx.attr.main_class, + rjars = rjars, + use_jacoco = True, wrapper = wrapper, + )) + + return struct( + files = out.files, + instrumented_files = out.instrumented_files, + providers = out.providers, + runfiles = ctx.runfiles(coverage_runfiles, transitive_files = out.runfiles.files), + scala = out.scala, ) - return out def _gen_test_suite_flags_based_on_prefixes_and_suffixes(ctx, archives): return struct( - testSuiteFlag = "-Dbazel.test_suite=%s" % ctx.attr.suite_class, archiveFlag = "-Dbazel.discover.classes.archives.file.paths=%s" % archives, prefixesFlag = "-Dbazel.discover.classes.prefixes=%s" % ",".join( ctx.attr.prefixes, ), + printFlag = "-Dbazel.discover.classes.print.discovered=%s" % + ctx.attr.print_discovered_classes, suffixesFlag = "-Dbazel.discover.classes.suffixes=%s" % ",".join( ctx.attr.suffixes, ), - printFlag = "-Dbazel.discover.classes.print.discovered=%s" % - ctx.attr.print_discovered_classes, + testSuiteFlag = "-Dbazel.test_suite=%s" % ctx.attr.suite_class, ) def _serialize_archives_short_path(archives): @@ -1121,9 +1201,9 @@ def scala_junit_test_impl(ctx): wrapper, implicit_junit_deps_needed_for_java_compilation = implicit_junit_deps_needed_for_java_compilation, - unused_dependency_checker_mode = unused_dependency_checker_mode, unused_dependency_checker_ignored_targets = unused_dependency_checker_ignored_targets, + unused_dependency_checker_mode = unused_dependency_checker_mode, ) if ctx.attr.tests_from: @@ -1146,10 +1226,58 @@ def scala_junit_test_impl(ctx): ] _write_executable( ctx = ctx, - rjars = out.transitive_rjars, - main_class = "com.google.testing.junit.runner.BazelTestRunner", jvm_flags = launcherJvmFlags + ctx.attr.jvm_flags, + main_class = "com.google.testing.junit.runner.BazelTestRunner", + rjars = out.transitive_rjars, wrapper = wrapper, ) return out + +def _jacoco_offline_instrument(ctx, input_jar): + if not ctx.configuration.coverage_enabled or not hasattr(ctx.attr, "_code_coverage_instrumentation_worker"): + return _empty_coverage_struct + + worker_inputs, _, worker_input_manifests = ctx.resolve_command( + tools = [ctx.attr._code_coverage_instrumentation_worker], + ) + + output_jar = ctx.actions.declare_file( + "{}-offline.jar".format(input_jar.basename.split(".")[0]), + ) + in_out_pairs = [ + (input_jar, output_jar), + ] + + args = ctx.actions.args() + args.add_all(in_out_pairs, map_each = _jacoco_offline_instrument_format_each) + args.set_param_file_format("multiline") + args.use_param_file("@%s", use_always = True) + + ctx.actions.run( + mnemonic = "JacocoInstrumenter", + inputs = [in_out_pair[0] for in_out_pair in in_out_pairs] + worker_inputs, + outputs = [in_out_pair[1] for in_out_pair in in_out_pairs], + executable = ctx.attr._code_coverage_instrumentation_worker.files_to_run.executable, + input_manifests = worker_input_manifests, + execution_requirements = {"supports-workers": "1"}, + arguments = [args], + ) + + replacements = {i: o for (i, o) in in_out_pairs} + provider = _coverage_replacements_provider.create( + replacements = replacements, + ) + + return struct( + instrumented_files = struct( + dependency_attributes = _coverage_replacements_provider.dependency_attributes, + extensions = ["scala", "java"], + source_attributes = ["srcs"], + ), + providers = [provider], + replacements = replacements, + ) + +def _jacoco_offline_instrument_format_each(in_out_pair): + return (["%s=%s" % (in_out_pair[0].path, in_out_pair[1].path)]) diff --git a/scala/scala.bzl b/scala/scala.bzl index c4694cc31..deadff550 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -8,6 +8,10 @@ load( _scala_repl_impl = "scala_repl_impl", _scala_test_impl = "scala_test_impl", ) +load( + "@io_bazel_rules_scala//scala/private:coverage_replacements_provider.bzl", + _coverage_replacements_provider = "coverage_replacements_provider", +) load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") load( "@io_bazel_rules_scala//scala:scala_maven_import_external.bzl", @@ -108,8 +112,15 @@ _junit_resolve_deps = { # Common attributes reused across multiple rules. _common_attrs_for_plugin_bootstrapping = { - "srcs": attr.label_list(allow_files = [".scala", ".srcjar", ".java"]), - "deps": attr.label_list(aspects = [_collect_plus_one_deps_aspect]), + "srcs": attr.label_list(allow_files = [ + ".scala", + ".srcjar", + ".java", + ]), + "deps": attr.label_list(aspects = [ + _collect_plus_one_deps_aspect, + _coverage_replacements_provider.aspect, + ]), "plugins": attr.label_list(allow_files = [".jar"]), "runtime_deps": attr.label_list(providers = [[JavaInfo]]), "data": attr.label_list(allow_files = True), @@ -121,12 +132,20 @@ _common_attrs_for_plugin_bootstrapping = { "jvm_flags": attr.string_list(), "scalac_jvm_flags": attr.string_list(), "javac_jvm_flags": attr.string_list(), - "expect_java_output": attr.bool(default = True, mandatory = False), - "print_compile_time": attr.bool(default = False, mandatory = False), + "expect_java_output": attr.bool( + default = True, + mandatory = False, + ), + "print_compile_time": attr.bool( + default = False, + mandatory = False, + ), } _common_attrs = {} + _common_attrs.update(_common_attrs_for_plugin_bootstrapping) + _common_attrs.update({ # using stricts scala deps is done by using command line flag called 'strict_java_deps' # switching mode to "on" means that ANY API change in a target's transitive dependencies will trigger a recompilation of that target, @@ -139,7 +158,12 @@ _common_attrs.update({ mandatory = False, ), "unused_dependency_checker_mode": attr.string( - values = ["warn", "error", "off", ""], + values = [ + "warn", + "error", + "off", + "", + ], mandatory = False, ), "_unused_dependency_checker_plugin": attr.label( @@ -150,11 +174,20 @@ _common_attrs.update({ mandatory = False, ), "unused_dependency_checker_ignored_targets": attr.label_list(default = []), + "_code_coverage_instrumentation_worker": attr.label( + default = "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/coverage/instrumenter", + allow_files = True, + executable = True, + cfg = "host", + ), }) _library_attrs = { "main_class": attr.string(), - "exports": attr.label_list(allow_files = False), + "exports": attr.label_list( + allow_files = False, + aspects = [_coverage_replacements_provider.aspect], + ), } _common_outputs = { @@ -165,79 +198,103 @@ _common_outputs = { } _library_outputs = {} + _library_outputs.update(_common_outputs) _scala_library_attrs = {} + _scala_library_attrs.update(_implicit_deps) + _scala_library_attrs.update(_common_attrs) + _scala_library_attrs.update(_library_attrs) + _scala_library_attrs.update(_resolve_deps) scala_library = rule( - implementation = _scala_library_impl, attrs = _scala_library_attrs, - outputs = _library_outputs, fragments = ["java"], + outputs = _library_outputs, toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_library_impl, ) # the scala compiler plugin used for dependency analysis is compiled using `scala_library`. # in order to avoid cyclic dependencies `scala_library_for_plugin_bootstrapping` was created for this purpose, # which does not contain plugin related attributes, and thus avoids the cyclic dependency issue _scala_library_for_plugin_bootstrapping_attrs = {} + _scala_library_for_plugin_bootstrapping_attrs.update(_implicit_deps) + _scala_library_for_plugin_bootstrapping_attrs.update(_library_attrs) + _scala_library_for_plugin_bootstrapping_attrs.update(_resolve_deps) + _scala_library_for_plugin_bootstrapping_attrs.update( _common_attrs_for_plugin_bootstrapping, ) + scala_library_for_plugin_bootstrapping = rule( - implementation = _scala_library_for_plugin_bootstrapping_impl, attrs = _scala_library_for_plugin_bootstrapping_attrs, - outputs = _library_outputs, fragments = ["java"], + outputs = _library_outputs, toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_library_for_plugin_bootstrapping_impl, ) _scala_macro_library_attrs = { "main_class": attr.string(), "exports": attr.label_list(allow_files = False), } + _scala_macro_library_attrs.update(_implicit_deps) + _scala_macro_library_attrs.update(_common_attrs) + _scala_macro_library_attrs.update(_library_attrs) + _scala_macro_library_attrs.update(_resolve_deps) # Set unused_dependency_checker_mode default to off for scala_macro_library _scala_macro_library_attrs["unused_dependency_checker_mode"] = attr.string( default = "off", - values = ["warn", "error", "off", ""], + values = [ + "warn", + "error", + "off", + "", + ], mandatory = False, ) scala_macro_library = rule( - implementation = _scala_macro_library_impl, attrs = _scala_macro_library_attrs, - outputs = _common_outputs, fragments = ["java"], + outputs = _common_outputs, toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_macro_library_impl, ) _scala_binary_attrs = { "main_class": attr.string(mandatory = True), "classpath_resources": attr.label_list(allow_files = True), } + _scala_binary_attrs.update(_launcher_template) + _scala_binary_attrs.update(_implicit_deps) + _scala_binary_attrs.update(_common_attrs) + _scala_binary_attrs.update(_resolve_deps) + scala_binary = rule( - implementation = _scala_binary_impl, attrs = _scala_binary_attrs, - outputs = _common_outputs, executable = True, fragments = ["java"], + outputs = _common_outputs, toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_binary_impl, ) _scala_test_attrs = { @@ -259,33 +316,49 @@ _scala_test_attrs = { "_scalatest_reporter": attr.label( default = Label("//scala/support:test_reporter"), ), + "_jacocorunner": attr.label( + default = Label("@bazel_tools//tools/jdk:JacocoCoverage"), + ), + "_lcov_merger": attr.label( + default = Label("@bazel_tools//tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator:Main"), + ), } + _scala_test_attrs.update(_launcher_template) + _scala_test_attrs.update(_implicit_deps) + _scala_test_attrs.update(_common_attrs) + _scala_test_attrs.update(_test_resolve_deps) + scala_test = rule( - implementation = _scala_test_impl, attrs = _scala_test_attrs, - outputs = _common_outputs, executable = True, - test = True, fragments = ["java"], + outputs = _common_outputs, + test = True, toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_test_impl, ) _scala_repl_attrs = {} + _scala_repl_attrs.update(_launcher_template) + _scala_repl_attrs.update(_implicit_deps) + _scala_repl_attrs.update(_common_attrs) + _scala_repl_attrs.update(_resolve_deps) + scala_repl = rule( - implementation = _scala_repl_impl, attrs = _scala_repl_attrs, - outputs = _common_outputs, executable = True, fragments = ["java"], + outputs = _common_outputs, toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_repl_impl, ) def _default_scala_extra_jars(): @@ -339,9 +412,9 @@ def scala_repositories( major_version = _extract_major_version(scala_version) _new_scala_default_repository( + maven_servers = maven_servers, scala_version = scala_version, scala_version_jar_shas = scala_version_jar_shas, - maven_servers = maven_servers, ) scala_version_extra_jars = scala_extra_jars[major_version] @@ -369,8 +442,7 @@ def scala_repositories( _scala_maven_import_external( name = "io_bazel_rules_scala_scala_xml", - artifact = "org.scala-lang.modules:scala-xml_{major_version}:{extra_jar_version}" - .format( + artifact = "org.scala-lang.modules:scala-xml_{major_version}:{extra_jar_version}".format( major_version = major_version, extra_jar_version = scala_version_extra_jars["scala_xml"]["version"], ), @@ -382,8 +454,7 @@ def scala_repositories( _scala_maven_import_external( name = "io_bazel_rules_scala_scala_parser_combinators", artifact = - "org.scala-lang.modules:scala-parser-combinators_{major_version}:{extra_jar_version}" - .format( + "org.scala-lang.modules:scala-parser-combinators_{major_version}:{extra_jar_version}".format( major_version = major_version, extra_jar_version = scala_version_extra_jars["scala_parser_combinators"]["version"], ), @@ -409,6 +480,22 @@ def scala_repositories( 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", + jar_sha256 = "ecf1ad8192926438d0748bfcc3f09bebc7387d2a4184bb3a171a26084677e808", + licenses = ["notice"], + server_urls = maven_servers, + ) + + _scala_maven_import_external( + name = "io_bazel_rules_scala_org_ow2_asm_asm_debug_all", + artifact = "org.ow2.asm:asm-debug-all:5.0.1", + jar_sha256 = "4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57", + licenses = ["notice"], + server_urls = maven_servers, + ) + # Template for binary launcher BAZEL_JAVA_LAUNCHER_VERSION = "0.17.1" java_stub_template_url = ( @@ -419,12 +506,12 @@ def scala_repositories( ) http_file( name = "java_stub_template", + sha256 = + "39097bdc47407232e0fe7eed4f2c175c067b7eda95873cb76ffa76f1b4c18895", urls = [ "https://mirror.bazel.build/%s" % java_stub_template_url, "https://%s" % java_stub_template_url, ], - sha256 = - "39097bdc47407232e0fe7eed4f2c175c067b7eda95873cb76ffa76f1b4c18895", ) native.bind( @@ -519,9 +606,9 @@ def scala_library_suite( ts.append(n) scala_library( name = name, - deps = ts, - exports = exports + ts, visibility = visibility, + exports = exports + ts, + deps = ts, ) _scala_junit_test_attrs = { @@ -535,7 +622,10 @@ _scala_junit_test_attrs = { "suite_class": attr.string( default = "io.bazel.rulesscala.test_discovery.DiscoveredTestSuite", ), - "print_discovered_classes": attr.bool(default = False, mandatory = False), + "print_discovered_classes": attr.bool( + default = False, + mandatory = False, + ), "_junit": attr.label( default = Label( "//external:io_bazel_rules_scala/dependency/junit/junit", @@ -553,20 +643,26 @@ _scala_junit_test_attrs = { allow_files = True, ), } + _scala_junit_test_attrs.update(_launcher_template) + _scala_junit_test_attrs.update(_implicit_deps) + _scala_junit_test_attrs.update(_common_attrs) + _scala_junit_test_attrs.update(_junit_resolve_deps) + _scala_junit_test_attrs.update({ "tests_from": attr.label_list(providers = [[JavaInfo]]), }) + scala_junit_test = rule( - implementation = _scala_junit_test_impl, attrs = _scala_junit_test_attrs, + fragments = ["java"], outputs = _common_outputs, test = True, - fragments = ["java"], toolchains = ["@io_bazel_rules_scala//scala:toolchain_type"], + implementation = _scala_junit_test_impl, ) def scala_specs2_junit_test(name, **kwargs): diff --git a/src/java/io/bazel/rulesscala/coverage/instrumenter/BUILD b/src/java/io/bazel/rulesscala/coverage/instrumenter/BUILD new file mode 100644 index 000000000..a24d9726a --- /dev/null +++ b/src/java/io/bazel/rulesscala/coverage/instrumenter/BUILD @@ -0,0 +1,29 @@ +java_binary( + name = "instrumenter", + srcs = [ + "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/coverage/instrumenter:instrumenter_files", + ], + javacopts = [ + "-source 1.8", + "-target 1.8", + ], + main_class = "io.bazel.rulesscala.coverage.instrumenter.JacocoInstrumenter", + visibility = ["//visibility:public"], + runtime_deps = [ + "@io_bazel_rules_scala_org_ow2_asm_asm_debug_all", + ], + deps = [ + "@io_bazel_rules_scala//src/java/com/google/devtools/build/lib:worker", + "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/jar", + "@io_bazel_rules_scala//src/java/io/bazel/rulesscala/worker", + "@io_bazel_rules_scala_org_jacoco_org_jacoco_core", + ], +) + +filegroup( + name = "instrumenter_files", + srcs = [ + "JacocoInstrumenter.java", + ], + visibility = ["//visibility:public"], +) diff --git a/src/java/io/bazel/rulesscala/coverage/instrumenter/JacocoInstrumenter.java b/src/java/io/bazel/rulesscala/coverage/instrumenter/JacocoInstrumenter.java new file mode 100644 index 000000000..86c450b63 --- /dev/null +++ b/src/java/io/bazel/rulesscala/coverage/instrumenter/JacocoInstrumenter.java @@ -0,0 +1,105 @@ +package io.bazel.rulesscala.coverage.instrumenter; + +import io.bazel.rulesscala.jar.JarCreator; +import io.bazel.rulesscala.worker.GenericWorker; +import io.bazel.rulesscala.worker.Processor; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitor; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.jacoco.core.instr.Instrumenter; +import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator; + +public final class JacocoInstrumenter implements Processor { + + public static void main(String[] args) throws Exception { + (new Worker()).run(args); + } + + private static final class Worker extends GenericWorker { + public Worker() { + super(new JacocoInstrumenter()); + } + } + + @Override + public void processRequest(List < String > args) { + Instrumenter jacoco = new Instrumenter(new OfflineInstrumentationAccessGenerator()); + args.forEach(arg -> { + try { + processArg(jacoco, arg); + } catch (final Exception e) { + throw new RuntimeException(e); + } + }); + } + + private void processArg(Instrumenter jacoco, String arg) throws Exception { + String[] parts = arg.split("="); + if (parts.length != 2) { + throw new Exception("expected `in_path=out_path` form for argument: " + arg); + } + Path inPath = Paths.get(parts[0]); + Path outPath = Paths.get(parts[1]); + try ( + FileSystem inFS = FileSystems.newFileSystem(inPath, null); FileSystem outFS = FileSystems.newFileSystem( + URI.create("jar:" + outPath.toUri()), Collections.singletonMap("create", "true")); + ) { + FileVisitor fileVisitor = createInstrumenterVisitor(jacoco, outFS); + inFS.getRootDirectories().forEach(root -> { + try { + Files.walkFileTree(root, fileVisitor); + } catch (final Exception e) { + throw new RuntimeException(e); + } + }); + } + } + + private SimpleFileVisitor createInstrumenterVisitor(Instrumenter jacoco, FileSystem outFS) { + return new SimpleFileVisitor () { + @Override + public FileVisitResult visitFile(Path inPath, BasicFileAttributes attrs) { + try { + return actuallyVisitFile(inPath, attrs); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + private FileVisitResult actuallyVisitFile(Path inPath, BasicFileAttributes attrs) throws Exception { + Path outPath = outFS.getPath(inPath.toString()); + Files.createDirectories(outPath.getParent()); + if (inPath.toString().endsWith(".class")) { + try ( + BufferedInputStream inStream = new BufferedInputStream( + Files.newInputStream(inPath)); BufferedOutputStream outStream = new BufferedOutputStream( + Files.newOutputStream(outPath, StandardOpenOption.CREATE_NEW)); + ) { + jacoco.instrument(inStream, outStream, inPath.toString()); + } + Files.copy(inPath, outFS.getPath(outPath.toString() + ".uninstrumented")); + } else { + Files.copy(inPath, outPath); + } + return FileVisitResult.CONTINUE; + } + }; + } +} diff --git a/test/coverage/A1.scala b/test/coverage/A1.scala new file mode 100644 index 000000000..2dcc34f43 --- /dev/null +++ b/test/coverage/A1.scala @@ -0,0 +1,5 @@ +object A1 { + def a1(flag: Boolean): B1.type = + if (flag) B1 + else sys.error("oh noes") +} diff --git a/test/coverage/A2.scala b/test/coverage/A2.scala new file mode 100644 index 000000000..20e8b7a1e --- /dev/null +++ b/test/coverage/A2.scala @@ -0,0 +1,5 @@ +object A2 { + def a2(): Unit = { + println("a2: " + B2.b2_a()) + } +} diff --git a/test/coverage/B1.scala b/test/coverage/B1.scala new file mode 100644 index 000000000..3b5a9f305 --- /dev/null +++ b/test/coverage/B1.scala @@ -0,0 +1,7 @@ +object B1 { + + def not_called(): Unit = { + println("hello world") + } + +} diff --git a/test/coverage/B2.java b/test/coverage/B2.java new file mode 100644 index 000000000..30b97c543 --- /dev/null +++ b/test/coverage/B2.java @@ -0,0 +1,9 @@ +class B2 { + public static String b2_a() { + return C2.c2("hello from b2_a"); + } + + public static void b2_b() { + System.out.println("this is b2_b"); + } +} diff --git a/test/coverage/BUILD b/test/coverage/BUILD new file mode 100644 index 000000000..b5de53494 --- /dev/null +++ b/test/coverage/BUILD @@ -0,0 +1,78 @@ +load("//scala:scala.bzl", "scala_library", "scala_test") + +scala_test( + name = "test-all", + srcs = [ + "TestAll.scala", + ], + deps = [ + ":a1", + ":a2", + ":b1", + ], +) + +java_test( + name = "test-b2", + srcs = [ + "TestB2.java", + ], + test_class = "TestB2", + deps = [ + ":b2", + ], + tags = ["manual"], +) + +scala_library( + name = "a1", + srcs = [ + "A1.scala", + ], + deps = [ + ":b1", + ], +) + +scala_library( + name = "b1", + srcs = [ + "B1.scala", + ], +) + +scala_library( + name = "a2", + srcs = [ + "A2.scala", + ], + deps = [ + ":b2", + ], +) + +# +# As it stands I can't seem to generate coverage for Java libraries pulled into +# a scala_test target. +# +# The java_library is instrumented, but doesn't have the .uninstrumented files +# that Bazel seems to expect. There are a few code paths for code coverage, so +# down the road we can explore how to fix this... +# + +java_library( + name = "b2", + srcs = [ + "B2.java", + ], + deps = [ + ":c2", + ], +) + +scala_library( + name = "c2", + srcs = [ + "C2.scala", + ], +) diff --git a/test/coverage/C2.scala b/test/coverage/C2.scala new file mode 100644 index 000000000..7e405b1cf --- /dev/null +++ b/test/coverage/C2.scala @@ -0,0 +1,5 @@ +object C2 { + def c2(input: String): String = + input.reverse + +} diff --git a/test/coverage/TestAll.scala b/test/coverage/TestAll.scala new file mode 100644 index 000000000..cbe8f9e4e --- /dev/null +++ b/test/coverage/TestAll.scala @@ -0,0 +1,12 @@ +import org.scalatest._ + +class TestAll extends FlatSpec { + + "testA1" should "work" in { + assert(A1.a1(true) == B1) + } + + "testA2" should "work" in { + A2.a2() + } +} diff --git a/test/coverage/TestB2.java b/test/coverage/TestB2.java new file mode 100644 index 000000000..ea5275e64 --- /dev/null +++ b/test/coverage/TestB2.java @@ -0,0 +1,11 @@ +import org.junit.Test; +import org.junit.Assert.*; + +public class TestB2 { + + @Test + public void testB2() { + B2.b2_b(); + } + +} diff --git a/test/coverage/expected-coverage.dat b/test/coverage/expected-coverage.dat new file mode 100755 index 000000000..78827572f --- /dev/null +++ b/test/coverage/expected-coverage.dat @@ -0,0 +1,94 @@ +SF:/A1.scala +FN:-1,A1$:: ()V +FN:5,A1$:: ()V +FN:3,A1$::a1 (Z)LB1$; +FN:-1,A1::a1 (Z)LB1$; +FNDA:1,A1$:: ()V +FNDA:1,A1$:: ()V +FNDA:1,A1$::a1 (Z)LB1$; +FNDA:0,A1::a1 (Z)LB1$; +FNF:4 +FNH:3 +BA:3,2 +BRF:1 +BRH:1 +DA:3,4 +DA:4,0 +DA:5,5 +LH:2 +LF:3 +end_of_record +SF:/A2.scala +FN:-1,A2$:: ()V +FN:5,A2$:: ()V +FN:3,A2$::a2 ()V +FN:-1,A2::a2 ()V +FNDA:1,A2$:: ()V +FNDA:1,A2$:: ()V +FNDA:1,A2$::a2 ()V +FNDA:0,A2::a2 ()V +FNF:4 +FNH:3 +DA:3,11 +DA:5,5 +LH:2 +LF:2 +end_of_record +SF:/B1.scala +FN:-1,B1$:: ()V +FN:7,B1$:: ()V +FN:4,B1$::not_called ()V +FN:-1,B1::not_called ()V +FNDA:1,B1$:: ()V +FNDA:1,B1$:: ()V +FNDA:0,B1$::not_called ()V +FNDA:0,B1::not_called ()V +FNF:4 +FNH:2 +DA:4,0 +DA:7,5 +LH:1 +LF:2 +end_of_record +SF:/C2.scala +FN:-1,C2$:: ()V +FN:5,C2$:: ()V +FN:3,C2$::c2 (Ljava/lang/String;)Ljava/lang/String; +FN:-1,C2::c2 (Ljava/lang/String;)Ljava/lang/String; +FNDA:1,C2$:: ()V +FNDA:1,C2$:: ()V +FNDA:1,C2$::c2 (Ljava/lang/String;)Ljava/lang/String; +FNDA:1,C2::c2 (Ljava/lang/String;)Ljava/lang/String; +FNF:4 +FNH:4 +DA:3,9 +DA:5,5 +LH:2 +LF:2 +end_of_record +SF:/TestAll.scala +FN:10,TestAll$$anonfun$1:: (LTestAll;)V +FN:10,TestAll$$anonfun$1::apply ()V +FN:10,TestAll$$anonfun$1::apply$mcV$sp ()V +FN:6,TestAll$$anonfun$2:: (LTestAll;)V +FN:6,TestAll$$anonfun$2::apply ()Lorg/scalatest/compatible/Assertion; +FN:3,TestAll:: ()V +FNDA:1,TestAll$$anonfun$1:: (LTestAll;)V +FNDA:1,TestAll$$anonfun$1::apply ()V +FNDA:1,TestAll$$anonfun$1::apply$mcV$sp ()V +FNDA:1,TestAll$$anonfun$2:: (LTestAll;)V +FNDA:1,TestAll$$anonfun$2::apply ()Lorg/scalatest/compatible/Assertion; +FNDA:1,TestAll:: ()V +FNF:6 +FNH:6 +BA:6,2 +BRF:1 +BRH:1 +DA:3,2 +DA:5,22 +DA:6,51 +DA:9,23 +DA:10,13 +LH:5 +LF:5 +end_of_record diff --git a/test_rules_scala.sh b/test_rules_scala.sh index 3ee51945b..3188e006e 100755 --- a/test_rules_scala.sh +++ b/test_rules_scala.sh @@ -924,6 +924,13 @@ test_unused_dependency_fails_even_if_also_exists_in_plus_one_deps() { action_should_fail build --extra_toolchains="//test_expect_failure/plus_one_deps:plus_one_deps_with_unused_error" //test_expect_failure/plus_one_deps/with_unused_deps:a } +test_coverage() { + bazel coverage \ + //test/coverage/... + + diff test/coverage/expected-coverage.dat $(bazel info bazel-testlogs)/test/coverage/test-all/coverage.dat +} + assert_file_exists() { if [[ -f $1 ]]; then echo "File $1 exists." @@ -1024,4 +1031,4 @@ $runner test_compilation_succeeds_with_plus_one_deps_on_also_for_exports $runner test_plus_one_deps_only_works_for_java_info_targets $runner bazel test //test/... --extra_toolchains="//test_expect_failure/plus_one_deps:plus_one_deps" $runner test_unused_dependency_fails_even_if_also_exists_in_plus_one_deps - +$runner test_coverage diff --git a/tools/bazel b/tools/bazel new file mode 100755 index 000000000..6b9efe8e2 --- /dev/null +++ b/tools/bazel @@ -0,0 +1,111 @@ +#!/bin/bash -e + +abs_path() { + perl -MCwd -le ' + for (@ARGV) { + if ($p = Cwd::abs_path $_) { + print $p; + } else { + warn "abs_path: $_: $!\n"; + $ret = 1; + } + } + exit $ret' "$@" +} +workspace=$(cd $(dirname "$0")/..; pwd) +root_workspace=$(cd $(dirname $(abs_path "$0"))/..; pwd) + +default_bazel_version='0.22.0' + +if [ -z "$BAZEL_VERSION" ]; then + bazel_version="$default_bazel_version" +else + bazel_version="$BAZEL_VERSION" +fi + +case "$bazel_version" in + 'host') + bazel_version=$("$BAZEL_REAL" version | awk '/Build label/ {print $3}' | cut -d '-' -f 1) + bazel="$BAZEL_REAL" + ;; + '0.21.0') + darwin_sha='5e40dcf12a18990ffe5830fb5c83297aed090fd6e6c7c5b2eb720c19a33044fc' + linux_sha='328d5fa87a61e1f6e674a8f88c5ae54b8987eaf5a6c944257600c5029c8feef8' + ;; + '0.22.0') + darwin_sha='adae5bbc3bf2b9b60d460d8ea2c65da1a6edd75b242439dfc49d001272548d13' + linux_sha='6d85f9fd6671ec5ffa8c78d150fe6c8da50874cfa8aa1900672dfd93e9989cb0' + ;; + *) + echo "The requested Bazel version '$bazel_version' is not supported" + exit 1 + ;; +esac + +if [ -z "$bazel" ]; then + bazel_bin_loc=~/.bazel_binaries + mkdir -p $bazel_bin_loc + bazel=$bazel_bin_loc/$bazel_version/bin/bazel-real +fi + +if ! [ -f "$bazel" ]; then + case $(uname -s) in + Darwin) + platform='darwin' + sha=$darwin_sha + ;; + Linux) + platform='linux' + sha=$linux_sha + ;; + *) + echo 'Your OS is not supported.' + exit 1 + ;; + esac + remote_source=https://github.com/bazelbuild/bazel/releases/download + installer_name="bazel-$bazel_version-installer-$platform-x86_64.sh" + url="$remote_source/$bazel_version/$installer_name" + ( + tmp_dir=$(mktemp -d) + trap "rm -rf $tmp_dir" EXIT + cd $tmp_dir + (>&2 echo "downloading installer from") + (>&2 echo "$url") + curl -o installer.sh -L $url + generated_sha=$(shasum -a 256 installer.sh | awk '{print $1}') + if [ "$generated_sha" != "$sha" ]; then + echo "Sha 256 does not match, expected: $sha" + echo "But found $generated_sha" + echo "Recommend you: update the sha to the expected" + echo "and then re-run this script" + exit 1 + fi + chmod +x installer.sh + ./installer.sh --base=$bazel_bin_loc/$bazel_version --bin=$bazel_bin_loc/$bazel_version/bin_t + ) +fi + +extra_command_args=() + +IFS=. read -r major minor patch < <(echo "$bazel_version") +#extra_command_args+=("--config=v$major.$minor") + +for (( i=1; i<=$#; i++ )) +do + case "${!i}" in + -*) + ;; + *) + n=$(($i + 1)) + set -- "${@:1:$i}" "${extra_command_args[@]}" "${@:$n}" + break + ;; + esac +done + +disk_cache="$root_workspace"/.bazel_cache +mkdir -p "$disk_cache" + +(>&2 echo :: exec "$bazel" "$@") +exec "$bazel" "$@" From 6810070126ae065a26478443275c2f01c79e72ea Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Tue, 26 Feb 2019 14:41:02 -0800 Subject: [PATCH 02/11] Fix conditional bug --- scala/private/rule_impls.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl index 4ce3b3451..237ada74e 100644 --- a/scala/private/rule_impls.bzl +++ b/scala/private/rule_impls.bzl @@ -233,7 +233,7 @@ CurrentTarget: {current_target} current_target = current_target, ) if is_dependency_analyzer_off(ctx) and not _is_plus_one_deps_off(ctx): - compiler_classpath_jars = transitive_compile_jars + compiler_classpath_jars = transitive_compile_jars plugins_list = plugins.to_list() plugin_arg = _join_path(plugins_list) @@ -1109,7 +1109,7 @@ def scala_test_impl(ctx): jvm_flags = ctx.attr.jvm_flags, main_class = ctx.attr.main_class, rjars = rjars, - use_jacoco = True, + use_jacoco = ctx.configuration.coverage_enabled, wrapper = wrapper, )) From c2ed1e71295f32e01fada9702557fc3630c53096 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Fri, 1 Mar 2019 07:16:46 -0800 Subject: [PATCH 03/11] Remove accidental tools/bazel launcher --- tools/bazel | 111 ---------------------------------------------------- 1 file changed, 111 deletions(-) delete mode 100755 tools/bazel diff --git a/tools/bazel b/tools/bazel deleted file mode 100755 index 6b9efe8e2..000000000 --- a/tools/bazel +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash -e - -abs_path() { - perl -MCwd -le ' - for (@ARGV) { - if ($p = Cwd::abs_path $_) { - print $p; - } else { - warn "abs_path: $_: $!\n"; - $ret = 1; - } - } - exit $ret' "$@" -} -workspace=$(cd $(dirname "$0")/..; pwd) -root_workspace=$(cd $(dirname $(abs_path "$0"))/..; pwd) - -default_bazel_version='0.22.0' - -if [ -z "$BAZEL_VERSION" ]; then - bazel_version="$default_bazel_version" -else - bazel_version="$BAZEL_VERSION" -fi - -case "$bazel_version" in - 'host') - bazel_version=$("$BAZEL_REAL" version | awk '/Build label/ {print $3}' | cut -d '-' -f 1) - bazel="$BAZEL_REAL" - ;; - '0.21.0') - darwin_sha='5e40dcf12a18990ffe5830fb5c83297aed090fd6e6c7c5b2eb720c19a33044fc' - linux_sha='328d5fa87a61e1f6e674a8f88c5ae54b8987eaf5a6c944257600c5029c8feef8' - ;; - '0.22.0') - darwin_sha='adae5bbc3bf2b9b60d460d8ea2c65da1a6edd75b242439dfc49d001272548d13' - linux_sha='6d85f9fd6671ec5ffa8c78d150fe6c8da50874cfa8aa1900672dfd93e9989cb0' - ;; - *) - echo "The requested Bazel version '$bazel_version' is not supported" - exit 1 - ;; -esac - -if [ -z "$bazel" ]; then - bazel_bin_loc=~/.bazel_binaries - mkdir -p $bazel_bin_loc - bazel=$bazel_bin_loc/$bazel_version/bin/bazel-real -fi - -if ! [ -f "$bazel" ]; then - case $(uname -s) in - Darwin) - platform='darwin' - sha=$darwin_sha - ;; - Linux) - platform='linux' - sha=$linux_sha - ;; - *) - echo 'Your OS is not supported.' - exit 1 - ;; - esac - remote_source=https://github.com/bazelbuild/bazel/releases/download - installer_name="bazel-$bazel_version-installer-$platform-x86_64.sh" - url="$remote_source/$bazel_version/$installer_name" - ( - tmp_dir=$(mktemp -d) - trap "rm -rf $tmp_dir" EXIT - cd $tmp_dir - (>&2 echo "downloading installer from") - (>&2 echo "$url") - curl -o installer.sh -L $url - generated_sha=$(shasum -a 256 installer.sh | awk '{print $1}') - if [ "$generated_sha" != "$sha" ]; then - echo "Sha 256 does not match, expected: $sha" - echo "But found $generated_sha" - echo "Recommend you: update the sha to the expected" - echo "and then re-run this script" - exit 1 - fi - chmod +x installer.sh - ./installer.sh --base=$bazel_bin_loc/$bazel_version --bin=$bazel_bin_loc/$bazel_version/bin_t - ) -fi - -extra_command_args=() - -IFS=. read -r major minor patch < <(echo "$bazel_version") -#extra_command_args+=("--config=v$major.$minor") - -for (( i=1; i<=$#; i++ )) -do - case "${!i}" in - -*) - ;; - *) - n=$(($i + 1)) - set -- "${@:1:$i}" "${extra_command_args[@]}" "${@:$n}" - break - ;; - esac -done - -disk_cache="$root_workspace"/.bazel_cache -mkdir -p "$disk_cache" - -(>&2 echo :: exec "$bazel" "$@") -exec "$bazel" "$@" From 721b7ec9f3b67a2d6d7c224b32905bdb7979ad97 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Fri, 1 Mar 2019 07:22:59 -0800 Subject: [PATCH 04/11] Get rid of default argument --- scala/private/rule_impls.bzl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl index 237ada74e..c2ce47451 100644 --- a/scala/private/rule_impls.bzl +++ b/scala/private/rule_impls.bzl @@ -233,7 +233,7 @@ CurrentTarget: {current_target} current_target = current_target, ) if is_dependency_analyzer_off(ctx) and not _is_plus_one_deps_off(ctx): - compiler_classpath_jars = transitive_compile_jars + compiler_classpath_jars = transitive_compile_jars plugins_list = plugins.to_list() plugin_arg = _join_path(plugins_list) @@ -583,7 +583,7 @@ JAVA_EXEC_TO_USE=${{REAL_EXTERNAL_JAVA_BIN:-$DEFAULT_JAVABIN}} ) return wrapper -def _write_executable(ctx, rjars, main_class, jvm_flags, wrapper, use_jacoco = False): +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( @@ -960,6 +960,7 @@ def scala_binary_impl(ctx): jvm_flags = ctx.attr.jvm_flags, main_class = ctx.attr.main_class, rjars = out.transitive_rjars, + use_jacoco = False, wrapper = wrapper, ) return out @@ -1017,6 +1018,7 @@ trap finish EXIT jvm_flags = ["-Dscala.usejavacp=true"] + ctx.attr.jvm_flags, main_class = "scala.tools.nsc.MainGenericRunner", rjars = out.transitive_rjars, + use_jacoco = False, wrapper = wrapper, ) @@ -1229,6 +1231,7 @@ def scala_junit_test_impl(ctx): jvm_flags = launcherJvmFlags + ctx.attr.jvm_flags, main_class = "com.google.testing.junit.runner.BazelTestRunner", rjars = out.transitive_rjars, + use_jacoco = False, wrapper = wrapper, ) From 1cbe4b0729673cdbacc73399a68c6ac961dadbdf Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Tue, 5 Mar 2019 14:59:31 -0800 Subject: [PATCH 05/11] Bump bazel versions to 0.18.x+ --- .travis.yml | 4 ++-- README.md | 8 ++++++-- scala/scala.bzl | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 675219116..e3dc83d2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,10 +19,10 @@ env: # See https://github.com/bazelbuild/rules_scala/pull/622 # we want to test the last release #- V=0.16.1 TEST_SCRIPT=test_lint.sh - - V=0.17.1 TEST_SCRIPT=test_rules_scala.sh + - V=0.18.0 TEST_SCRIPT=test_rules_scala.sh - V=0.22.0 TEST_SCRIPT=test_rules_scala.sh #- V=0.14.1 TEST_SCRIPT=test_intellij_aspect.sh - - V=0.17.1 TEST_SCRIPT=test_reproducibility.sh + - V=0.18.0 TEST_SCRIPT=test_reproducibility.sh - V=0.22.0 TEST_SCRIPT=test_reproducibility.sh before_install: diff --git a/README.md b/README.md index ee1dfd2ad..05bda5dcc 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,12 @@ for an example workspace using another scala version. | bazel | rules_scala gitsha | |-------|--------------------| -| 0.16.x | HEAD | +| 0.22.x | HEAD | +| 0.21.x | HEAD | +| 0.20.x | HEAD | +| 0.19.x | HEAD | +| 0.18.x | HEAD | +| 0.17.1 | 37032481ed5ac2b2d88a1a3f0d80be7b14beb19c | | 0.15.x | 3b9ab9be31ac217d3337c709cb6bfeb89c8dcbb1 | | 0.14.x | 3b9ab9be31ac217d3337c709cb6bfeb89c8dcbb1 | | 0.13.x | 3c987b6ae8a453886759b132f1572c0efca2eca2 | @@ -761,4 +766,3 @@ Here's a (non-exhaustive) list of companies that use `rules_scala` in production * [Stripe](https://stripe.com/) * [VSCO](https://vsco.co) * [Wix](https://www.wix.com/) - diff --git a/scala/scala.bzl b/scala/scala.bzl index deadff550..1f394720e 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -497,7 +497,7 @@ def scala_repositories( ) # Template for binary launcher - BAZEL_JAVA_LAUNCHER_VERSION = "0.17.1" + BAZEL_JAVA_LAUNCHER_VERSION = "0.22.0" java_stub_template_url = ( "raw.githubusercontent.com/bazelbuild/bazel/" + BAZEL_JAVA_LAUNCHER_VERSION + From f6dc48e7f898529a40dfd485a555eacd0a9ac9dc Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Tue, 5 Mar 2019 15:03:25 -0800 Subject: [PATCH 06/11] Add back 0.16.x hash --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 05bda5dcc..426f0c230 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,8 @@ for an example workspace using another scala version. | 0.20.x | HEAD | | 0.19.x | HEAD | | 0.18.x | HEAD | -| 0.17.1 | 37032481ed5ac2b2d88a1a3f0d80be7b14beb19c | +| 0.17.x | 37032481ed5ac2b2d88a1a3f0d80be7b14beb19c | +| 0.16.x | 1354d935a74395b3f0870dd90a04e0376fe22587 | | 0.15.x | 3b9ab9be31ac217d3337c709cb6bfeb89c8dcbb1 | | 0.14.x | 3b9ab9be31ac217d3337c709cb6bfeb89c8dcbb1 | | 0.13.x | 3c987b6ae8a453886759b132f1572c0efca2eca2 | From e291ad8bd25dd856e1ea86641a793dc949ac374e Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Tue, 5 Mar 2019 15:15:18 -0800 Subject: [PATCH 07/11] Revert launcher template version --- scala/scala.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scala/scala.bzl b/scala/scala.bzl index 1f394720e..deadff550 100644 --- a/scala/scala.bzl +++ b/scala/scala.bzl @@ -497,7 +497,7 @@ def scala_repositories( ) # Template for binary launcher - BAZEL_JAVA_LAUNCHER_VERSION = "0.22.0" + BAZEL_JAVA_LAUNCHER_VERSION = "0.17.1" java_stub_template_url = ( "raw.githubusercontent.com/bazelbuild/bazel/" + BAZEL_JAVA_LAUNCHER_VERSION + From 14b95935646f27a4f3b5bf8bf7d60adc78d994e7 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Tue, 5 Mar 2019 15:25:28 -0800 Subject: [PATCH 08/11] 18 -> 19 --- .travis.yml | 4 ++-- README.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3dc83d2a..dff56815e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,10 +19,10 @@ env: # See https://github.com/bazelbuild/rules_scala/pull/622 # we want to test the last release #- V=0.16.1 TEST_SCRIPT=test_lint.sh - - V=0.18.0 TEST_SCRIPT=test_rules_scala.sh + - V=0.19.0 TEST_SCRIPT=test_rules_scala.sh - V=0.22.0 TEST_SCRIPT=test_rules_scala.sh #- V=0.14.1 TEST_SCRIPT=test_intellij_aspect.sh - - V=0.18.0 TEST_SCRIPT=test_reproducibility.sh + - V=0.19.0 TEST_SCRIPT=test_reproducibility.sh - V=0.22.0 TEST_SCRIPT=test_reproducibility.sh before_install: diff --git a/README.md b/README.md index 426f0c230..51a7ec05a 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,6 @@ for an example workspace using another scala version. | 0.21.x | HEAD | | 0.20.x | HEAD | | 0.19.x | HEAD | -| 0.18.x | HEAD | | 0.17.x | 37032481ed5ac2b2d88a1a3f0d80be7b14beb19c | | 0.16.x | 1354d935a74395b3f0870dd90a04e0376fe22587 | | 0.15.x | 3b9ab9be31ac217d3337c709cb6bfeb89c8dcbb1 | From 9f0faf445bd0927626f3d476369cb61c24a98e48 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Wed, 20 Mar 2019 16:17:06 -0700 Subject: [PATCH 09/11] Feature flag coverage logic --- scala/private/coverage_replacements_provider.bzl | 13 ++++++++++--- scala/private/rule_impls.bzl | 4 ++-- scala/scala_toolchain.bzl | 5 +++++ test/coverage/BUILD | 13 +++++++++++++ test/coverage/expected-coverage.dat | 16 ---------------- test_rules_scala.sh | 15 +++++++++++++-- 6 files changed, 43 insertions(+), 23 deletions(-) diff --git a/scala/private/coverage_replacements_provider.bzl b/scala/private/coverage_replacements_provider.bzl index 0969a3398..b541b9ed5 100644 --- a/scala/private/coverage_replacements_provider.bzl +++ b/scala/private/coverage_replacements_provider.bzl @@ -59,22 +59,29 @@ def _from_ctx(ctx, base = {}): ) def _aspect_impl(target, ctx): - if JavaInfo not in target: - return [] - else: + if JavaInfo in target and _is_enabled(ctx): return [_from_ctx(ctx.rule)] + else: + return [] _aspect = aspect( attr_aspects = _dependency_attributes, implementation = _aspect_impl, ) +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" + coverage_replacements_provider = struct( aspect = _aspect, combine = _combine, create = _CoverageReplacements, dependency_attributes = _dependency_attributes, from_ctx = _from_ctx, + is_enabled = _is_enabled, ) # from bazel's skylib diff --git a/scala/private/rule_impls.bzl b/scala/private/rule_impls.bzl index 94e780002..9d89e8e02 100644 --- a/scala/private/rule_impls.bzl +++ b/scala/private/rule_impls.bzl @@ -595,7 +595,7 @@ def _write_executable(ctx, rjars, main_class, jvm_flags, wrapper, use_jacoco): wrapper.short_path, ) - if use_jacoco: + 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], ) @@ -1094,7 +1094,7 @@ def scala_test_impl(ctx): rjars = out.transitive_rjars coverage_runfiles = [] - if ctx.configuration.coverage_enabled: + if ctx.configuration.coverage_enabled and _coverage_replacements_provider.is_enabled(ctx): coverage_replacements = _coverage_replacements_provider.from_ctx( ctx, base = out.coverage.replacements, diff --git a/scala/scala_toolchain.bzl b/scala/scala_toolchain.bzl index c228bc4ba..157eae715 100644 --- a/scala/scala_toolchain.bzl +++ b/scala/scala_toolchain.bzl @@ -9,6 +9,7 @@ def _scala_toolchain_impl(ctx): 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, ) return [toolchain] @@ -28,5 +29,9 @@ scala_toolchain = rule( default = "off", values = ["off", "on"], ), + "enable_code_coverage_aspect": attr.string( + default = "off", + values = ["off", "on"], + ) }, ) diff --git a/test/coverage/BUILD b/test/coverage/BUILD index b5de53494..9c95ed14e 100644 --- a/test/coverage/BUILD +++ b/test/coverage/BUILD @@ -1,4 +1,17 @@ load("//scala:scala.bzl", "scala_library", "scala_test") +load("//scala:scala_toolchain.bzl", "scala_toolchain") + +scala_toolchain( + name = "enable_code_coverage_aspect_impl", + enable_code_coverage_aspect = "on", + visibility = ["//visibility:public"], +) +toolchain( + name = "enable_code_coverage_aspect", + toolchain = "enable_code_coverage_aspect_impl", + toolchain_type = "@io_bazel_rules_scala//scala:toolchain_type", + visibility = ["//visibility:public"], +) scala_test( name = "test-all", diff --git a/test/coverage/expected-coverage.dat b/test/coverage/expected-coverage.dat index 78827572f..9b1f7c60e 100755 --- a/test/coverage/expected-coverage.dat +++ b/test/coverage/expected-coverage.dat @@ -50,22 +50,6 @@ DA:7,5 LH:1 LF:2 end_of_record -SF:/C2.scala -FN:-1,C2$:: ()V -FN:5,C2$:: ()V -FN:3,C2$::c2 (Ljava/lang/String;)Ljava/lang/String; -FN:-1,C2::c2 (Ljava/lang/String;)Ljava/lang/String; -FNDA:1,C2$:: ()V -FNDA:1,C2$:: ()V -FNDA:1,C2$::c2 (Ljava/lang/String;)Ljava/lang/String; -FNDA:1,C2::c2 (Ljava/lang/String;)Ljava/lang/String; -FNF:4 -FNH:4 -DA:3,9 -DA:5,5 -LH:2 -LF:2 -end_of_record SF:/TestAll.scala FN:10,TestAll$$anonfun$1:: (LTestAll;)V FN:10,TestAll$$anonfun$1::apply ()V diff --git a/test_rules_scala.sh b/test_rules_scala.sh index 3188e006e..45e51f3aa 100755 --- a/test_rules_scala.sh +++ b/test_rules_scala.sh @@ -924,13 +924,22 @@ test_unused_dependency_fails_even_if_also_exists_in_plus_one_deps() { action_should_fail build --extra_toolchains="//test_expect_failure/plus_one_deps:plus_one_deps_with_unused_error" //test_expect_failure/plus_one_deps/with_unused_deps:a } -test_coverage() { +test_coverage_on() { bazel coverage \ + --extra_toolchains="//test/coverage:enable_code_coverage_aspect" \ //test/coverage/... - diff test/coverage/expected-coverage.dat $(bazel info bazel-testlogs)/test/coverage/test-all/coverage.dat } +test_coverage_off() { + # ensure coverage is disabled by default + bazel coverage \ + //test/coverage/... + if [ ! $("$(bazel info bazel-testlogs)"/test/coverage/test-all/coverage.dat | sed '/^\s*$/d' | wc -l) -eq 0 ]; then + exit 1 + fi +} + assert_file_exists() { if [[ -f $1 ]]; then echo "File $1 exists." @@ -946,6 +955,8 @@ dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) . "${dir}"/test_runner.sh runner=$(get_test_runner "${1:-local}") +$runner test_coverage_off +$runner test_coverage_on $runner bazel build test/... #$runner bazel build "test/... --all_incompatible_changes" $runner bazel test test/... From d2761bfd22a5609df705344c2f26774f1f381810 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Wed, 20 Mar 2019 16:21:19 -0700 Subject: [PATCH 10/11] Fix test script --- test_rules_scala.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test_rules_scala.sh b/test_rules_scala.sh index 45e51f3aa..8d44072b9 100755 --- a/test_rules_scala.sh +++ b/test_rules_scala.sh @@ -955,8 +955,6 @@ dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) . "${dir}"/test_runner.sh runner=$(get_test_runner "${1:-local}") -$runner test_coverage_off -$runner test_coverage_on $runner bazel build test/... #$runner bazel build "test/... --all_incompatible_changes" $runner bazel test test/... @@ -1042,4 +1040,5 @@ $runner test_compilation_succeeds_with_plus_one_deps_on_also_for_exports $runner test_plus_one_deps_only_works_for_java_info_targets $runner bazel test //test/... --extra_toolchains="//test_expect_failure/plus_one_deps:plus_one_deps" $runner test_unused_dependency_fails_even_if_also_exists_in_plus_one_deps -$runner test_coverage +$runner test_coverage_off +$runner test_coverage_on From d508a761a860b9f4e0849b6a489301686d0aa43f Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Wed, 20 Mar 2019 16:57:24 -0700 Subject: [PATCH 11/11] Remove negative case --- test_rules_scala.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test_rules_scala.sh b/test_rules_scala.sh index 8d44072b9..0164e9659 100755 --- a/test_rules_scala.sh +++ b/test_rules_scala.sh @@ -931,15 +931,6 @@ test_coverage_on() { diff test/coverage/expected-coverage.dat $(bazel info bazel-testlogs)/test/coverage/test-all/coverage.dat } -test_coverage_off() { - # ensure coverage is disabled by default - bazel coverage \ - //test/coverage/... - if [ ! $("$(bazel info bazel-testlogs)"/test/coverage/test-all/coverage.dat | sed '/^\s*$/d' | wc -l) -eq 0 ]; then - exit 1 - fi -} - assert_file_exists() { if [[ -f $1 ]]; then echo "File $1 exists." @@ -1040,5 +1031,4 @@ $runner test_compilation_succeeds_with_plus_one_deps_on_also_for_exports $runner test_plus_one_deps_only_works_for_java_info_targets $runner bazel test //test/... --extra_toolchains="//test_expect_failure/plus_one_deps:plus_one_deps" $runner test_unused_dependency_fails_even_if_also_exists_in_plus_one_deps -$runner test_coverage_off $runner test_coverage_on