Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

Rule for running onejar on plugins #208

Open
bjchambers opened this issue Aug 24, 2019 · 3 comments
Open

Rule for running onejar on plugins #208

bjchambers opened this issue Aug 24, 2019 · 3 comments

Comments

@bjchambers
Copy link
Contributor

If you attempt to use a scalac plugin such as scapegoat or silencer, you'll get the following warning from phase_classpaths:

WARNING! It is slightly inefficient to use a JVM target with dependencies directly as a scalac plugin. Please SingleJar the target before using it as a scalac plugin in order to avoid additional overhead.

[1] https://github.com/higherkindness/rules_scala/blob/master/rules/private/phases/phase_classpaths.bzl#L25

However, there is no available rule that makes it possible to "SingleJar" the target.

I wrote a quick rule that exposes the existing singlejar wrapper from rules_scala, but that feels a bit wrong since needs to load the action_singlejar from private/utils.bzl. It seems like it would make sense to add such a rule to make it easier to package plugins?

load("@rules_scala_annex//rules/common:private/utils.bzl", _action_singlejar = "action_singlejar")

def _scala_plugin_impl(ctx):
    plugin = ctx.attr.plugin
    plugin_runtime_jars = plugin[JavaInfo].transitive_runtime_jars.to_list()

    output_jar = ctx.outputs.plugin_singlejar
    _action_singlejar(
        ctx,
        inputs = plugin_runtime_jars,
        output = output_jar,
        progress_message = "singlejar scalac plugin %s" % plugin.label.name,
    )

    return struct(
        providers = [
            JavaInfo(
                output_jar = output_jar,
                compile_jar = output_jar,
            ),
        ],
    )

scala_plugin = rule(
    implementation = _scala_plugin_impl,
    attrs = {
        "plugin": attr.label(
            allow_single_file = True,
            mandatory = True,
            doc = "The Scalac plugin.",
            providers = [JavaInfo],
        ),
        "_singlejar": attr.label(
            cfg = "host",
            default = "@bazel_tools//tools/jdk:singlejar",
            executable = True,
        ),
    },
    outputs = {
        "plugin_singlejar": "%{name}_singlejar.jar",
    },
)
@SrodriguezO
Copy link
Collaborator

Hey @bjchambers! Thanks for opening this issue. For plugins that are only referenced directly in one package (like global plugins you specify when configuring the compiler) this shouldn't be an issue (since we only run the singlejar action once either way).

However, if you're specifying a plugin in multiple projects (for instance, directly in the plugins attribute of multiple scala_library/scala_binary targets) then the current approach would singlejar the plugin multiple times. A scala_plugin rule could help in those cases.

Are you seeing the warning multiple times?

@bjchambers
Copy link
Contributor Author

I'm only seeing the warning once, but I theorized that is because I have enabled de-duplicating messages. When I used the above to explicitly run onejar in the top-level BUILD where I was defining the global plugins, the total number of actions executed on a clean build was reduced by 57, which made me suspect that the onejar was actually being produced multiple times.

If this really only happens once, it would be good if there was a way to suppress the warning the first time. Could the code that produces this warning (and runs onejar) detect that is being used as part of setting the global plugins and not log?

@pauldraper
Copy link
Contributor

pauldraper commented Oct 14, 2019

scalac expects each plugin to be fully isolated, so we need to smash everything together with singlejar

Really? This contradicts sbt/sbt#2255 which claims that the entire plugin classpath is shared.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants