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 / ivy / ivy.py
Size: Mime:
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import os.path
from contextlib import contextmanager

from twitter.common.collections import maybe_list

from pants.java import util
from pants.java.distribution.distribution import DistributionLocator
from pants.java.executor import Executor, SubprocessExecutor
from pants.process.lock import OwnerPrintingInterProcessFileLock
from pants.util.dirutil import safe_mkdir


class Ivy:
    """Encapsulates the ivy cli taking care of the basic invocation letting you just worry about the
    args to pass to the cli itself.

    :API: public
    """

    class Error(Exception):
        """Indicates an error executing an ivy command."""

    def __init__(
        self, classpath, ivy_settings=None, ivy_resolution_cache_dir=None, extra_jvm_options=None
    ):
        """Configures an ivy wrapper for the ivy distribution at the given classpath.

        :param ivy_settings: path to find settings.xml file
        :param ivy_resolution_cache_dir: path to store downloaded ivy artifacts
        :param extra_jvm_options: list of strings to add to command line when invoking Ivy
        """
        self._classpath = maybe_list(classpath)
        self._ivy_settings = ivy_settings
        if self._ivy_settings and not isinstance(self._ivy_settings, str):
            raise ValueError(
                "ivy_settings must be a string, given {} of type {}".format(
                    self._ivy_settings, type(self._ivy_settings)
                )
            )

        self._ivy_resolution_cache_dir = ivy_resolution_cache_dir
        if not isinstance(self._ivy_resolution_cache_dir, str):
            raise ValueError(
                "ivy_resolution_cache_dir must be a string, given {} of type {}".format(
                    self._ivy_resolution_cache_dir, type(self._ivy_resolution_cache_dir)
                )
            )

        self._extra_jvm_options = extra_jvm_options or []
        self._lock = OwnerPrintingInterProcessFileLock(
            os.path.join(self._ivy_resolution_cache_dir, "pants_ivy.file_lock")
        )

    @property
    def ivy_settings(self):
        """Returns the ivysettings.xml path used by this `Ivy` instance.

        May be None if ivy's built in default ivysettings.xml of standard public resolvers is being
        used.
        """
        return self._ivy_settings

    @property
    def ivy_resolution_cache_dir(self):
        """Returns the ivy cache dir used by this `Ivy` instance."""
        return self._ivy_resolution_cache_dir

    @contextmanager
    def resolution_lock(self):
        safe_mkdir(self._ivy_resolution_cache_dir)
        with self._lock:
            yield

    def execute(
        self,
        jvm_options=None,
        args=None,
        executor=None,
        workunit_factory=None,
        workunit_name=None,
        workunit_labels=None,
    ):
        """Executes the ivy commandline client with the given args.

        Raises Ivy.Error if the command fails for any reason.
        :param executor: Java executor to run ivy with.
        """
        # NB(gmalmquist): It should be OK that we can't declare a subsystem_dependency in this file
        # (because it's just a plain old object), because Ivy is only constructed by Bootstrapper, which
        # makes an explicit call to IvySubsystem.global_instance() in its constructor, which in turn has
        # a declared dependency on DistributionLocator.
        executor = executor or SubprocessExecutor(DistributionLocator.cached())
        runner = self.runner(jvm_options=jvm_options, args=args, executor=executor)
        try:
            with self.resolution_lock():
                result = util.execute_runner(
                    runner, workunit_factory, workunit_name, workunit_labels
                )
            if result != 0:
                raise self.Error(
                    "Ivy command failed with exit code {}{}".format(
                        result, ": " + " ".join(args) if args else ""
                    )
                )
        except executor.Error as e:
            raise self.Error("Problem executing ivy: {}".format(e))

    def runner(self, jvm_options=None, args=None, executor=None):
        """Creates an ivy commandline client runner for the given args."""
        args = args or []
        if self._ivy_settings and "-settings" not in args:
            args = ["-settings", self._ivy_settings] + args

        options = list(jvm_options) if jvm_options else []
        if self._ivy_resolution_cache_dir and "-cache" not in args:
            # TODO(John Sirois): Currently this is a magic property to support hand-crafted <caches/> in
            # ivysettings.xml.  Ideally we'd support either simple -caches or these hand-crafted cases
            # instead of just hand-crafted.  Clean this up by taking over ivysettings.xml and generating
            # it from BUILD constructs.
            options += ["-Divy.cache.dir={}".format(self._ivy_resolution_cache_dir)]
        options += self._extra_jvm_options

        executor = executor or SubprocessExecutor(DistributionLocator.cached())
        if not isinstance(executor, Executor):
            raise ValueError(
                "The executor argument must be an Executor instance, given {} of type {}".format(
                    executor, type(executor)
                )
            )

        return executor.runner(
            classpath=self._classpath, main="org.apache.ivy.Main", jvm_options=options, args=args
        )