Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
pantsbuild.pants / backend / jvm / subsystems / scala_platform.py
Size: Mime:
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from collections import namedtuple

from pants.backend.jvm.subsystems.jvm_tool_mixin import JvmToolMixin
from pants.backend.jvm.subsystems.zinc_language_mixin import ZincLanguageMixin
from pants.backend.jvm.targets.jar_library import JarLibrary
from pants.build_graph.address import Address
from pants.build_graph.injectables_mixin import InjectablesMixin
from pants.java.jar.jar_dependency import JarDependency
from pants.subsystem.subsystem import Subsystem

# full_version - the full scala version to use.
major_version_info = namedtuple("major_version_info", ["full_version"])


# Note that the compiler has two roles here: as a tool (invoked by the compile task), and as a
# runtime library (when compiling plugins, which require the compiler library as a dependency).
scala_build_info = {
    "2.10": major_version_info(full_version="2.10.6"),
    "2.11": major_version_info(full_version="2.11.12"),
    "2.12": major_version_info(full_version="2.12.8"),
}


# Because scalastyle inspects only the sources, it needn't match the platform version.
scala_style_jar = JarDependency("org.scalastyle", "scalastyle_2.11", "0.8.0")


# TODO: Sort out JVM compile config model: https://github.com/pantsbuild/pants/issues/4483.
class ScalaPlatform(JvmToolMixin, ZincLanguageMixin, InjectablesMixin, Subsystem):
    """A scala platform.

    :API: public
    """

    options_scope = "scala"

    @classmethod
    def create_jardep(cls, name, version):
        return JarDependency(
            org="org.scala-lang", name=name, rev=scala_build_info[version].full_version
        )

    @classmethod
    def _create_runtime_jardep(cls, version):
        return cls.create_jardep("scala-library", version)

    @classmethod
    def _create_compiler_jardep(cls, version):
        return cls.create_jardep("scala-compiler", version)

    @classmethod
    def versioned_tool_name(cls, tool, version):
        if version == "custom":
            return tool
        else:
            return f"{tool}_{version.replace('.', '_')}"

    @classmethod
    def register_options(cls, register):
        def register_scala_compiler_tool(version):
            cls.register_jvm_tool(
                register,
                cls.versioned_tool_name("scalac", version),
                classpath=[cls._create_compiler_jardep(version)],
            )

        def register_scala_repl_tool(version, with_jline=False):
            classpath = [
                cls._create_compiler_jardep(version)
            ]  # Note: the REPL is in the compiler jar.
            if with_jline:
                jline_dep = JarDependency(
                    org="org.scala-lang", name="jline", rev=scala_build_info[version].full_version
                )
                classpath.append(jline_dep)
            cls.register_jvm_tool(
                register, cls.versioned_tool_name("scala-repl", version), classpath=classpath
            )

        def register_style_tool(version):
            cls.register_jvm_tool(
                register,
                cls.versioned_tool_name("scalastyle", version),
                classpath=[scala_style_jar],
            )

        super().register_options(register)

        register(
            "--scalac-plugins",
            advanced=True,
            type=list,
            fingerprint=True,
            help="Use these scalac plugins.",
        )
        register(
            "--scalac-plugin-args",
            advanced=True,
            type=dict,
            default={},
            fingerprint=True,
            help="Map from scalac plugin name to list of arguments for that plugin.",
        )
        cls.register_jvm_tool(
            register,
            "scalac-plugin-dep",
            classpath=[],
            help="Search for scalac plugins here, as well as in any " "explicit dependencies.",
        )

        register(
            "--version",
            advanced=True,
            default="2.12",
            choices=["2.10", "2.11", "2.12", "custom"],
            fingerprint=True,
            help="The scala platform version. If --version=custom, the targets "
            "//:scala-library, //:scalac, //:scala-repl and //:scalastyle will be used, "
            "and must exist.  Otherwise, defaults for the specified version will be used.",
        )

        register(
            "--suffix-version",
            advanced=True,
            default=None,
            help="Scala suffix to be used in `scala_jar` definitions. For example, specifying "
            "`2.11` or `2.12.0-RC1` would cause `scala_jar` lookups for artifacts with "
            "those suffixes.",
        )

        # Register the fixed version tools.
        register_scala_compiler_tool("2.10")
        register_scala_repl_tool("2.10", with_jline=True)  # 2.10 repl requires jline.
        register_style_tool("2.10")

        register_scala_compiler_tool("2.11")
        register_scala_repl_tool("2.11")
        register_style_tool("2.11")

        register_scala_compiler_tool("2.12")
        register_scala_repl_tool("2.12")
        register_style_tool("2.12")

        # Register the custom tools. We provide a dummy classpath, so that register_jvm_tool won't
        # require that a target with the given spec actually exist (not everyone will define custom
        # scala platforms). However if the custom tool is actually resolved, we want that to
        # fail with a useful error, hence the dummy jardep with rev=None.
        def register_custom_tool(key):
            dummy_jardep = JarDependency("missing spec", f" //:{key}")
            cls.register_jvm_tool(
                register, cls.versioned_tool_name(key, "custom"), classpath=[dummy_jardep]
            )

        register_custom_tool("scalac")
        register_custom_tool("scala-repl")
        register_custom_tool("scalastyle")

    def _tool_classpath(self, tool, products):
        """Return the proper classpath based on products and scala version."""
        return self.tool_classpath_entries_from_products(
            products, self.versioned_tool_name(tool, self.version), scope=self.options_scope,
        )

    def compiler_classpath_entries(self, products):
        """Returns classpath entries for the scalac tool."""
        return self._tool_classpath("scalac", products)

    def style_classpath(self, products):
        """Returns classpath as paths for scalastyle."""
        classpath_entries = self._tool_classpath("scalastyle", products)
        return [classpath_entry.path for classpath_entry in classpath_entries]

    @property
    def version(self):
        return self.get_options().version

    def suffix_version(self, name):
        """Appends the platform version to the given artifact name.

        Also validates that the name doesn't already end with the version.
        """
        if self.version == "custom":
            suffix = self.get_options().suffix_version
            if suffix:
                return f"{name}_{suffix}"
            else:
                raise RuntimeError(
                    "Suffix version must be specified if using a custom scala version. "
                    "Suffix version is used for bootstrapping jars.  If a custom "
                    "scala version is not specified, then the version specified in "
                    "--scala-suffix-version is used.  For example for Scala "
                    '2.10.7 you would use the suffix version "2.10".'
                )

        elif name.endswith(self.version):
            raise ValueError(
                'The name "{0}" should not be suffixed with the scala platform version '
                "({1}): it will be added automatically.".format(name, self.version)
            )
        return f"{name}_{self.version}"

    @property
    def repl(self):
        """Return the repl tool key."""
        return self.versioned_tool_name("scala-repl", self.version)

    def injectables(self, build_graph):
        if self.version == "custom":
            return

        specs_to_create = [
            ("scalac", self._create_compiler_jardep),
            ("scala-library", self._create_runtime_jardep),
        ]

        for spec_key, create_jardep_func in specs_to_create:
            address_spec = self.injectables_address_spec_for_key(spec_key)
            target_address = Address.parse(address_spec)
            if not build_graph.contains_address(target_address):
                jars = [create_jardep_func(self.version)]
                build_graph.inject_synthetic_target(
                    target_address, JarLibrary, jars=jars, scope="forced"
                )
            elif not build_graph.get_target(target_address).is_synthetic:
                raise build_graph.ManualSyntheticTargetError(target_address)

    @property
    def injectables_address_spec_mapping(self):
        maybe_suffix = "" if self.version == "custom" else "-synthetic"
        return {
            # Target spec for the scala compiler library.
            "scalac": [f"//:scalac{maybe_suffix}"],
            # Target spec for the scala runtime library.
            "scala-library": [f"//:scala-library{maybe_suffix}"],
        }