1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140

build / android / pylib / utils / maven_downloader.py [blame]

#!/usr/bin/env vpython3
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import errno
import logging
import os
import shutil
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
import devil_chromium  # pylint: disable=unused-import
from devil.utils import cmd_helper
from devil.utils import parallelizer


def _MakeDirsIfAbsent(path):
  try:
    os.makedirs(path)
  except OSError as err:
    if err.errno != errno.EEXIST or not os.path.isdir(path):
      raise


class MavenDownloader:
  '''
  Downloads and installs the requested artifacts from the Google Maven repo.
  The artifacts are expected to be specified in the format
  "group_id:artifact_id:version:file_type", as the default file type is JAR
  but most Android libraries are provided as AARs, which would otherwise fail
  downloading. See Install()
  '''

  # Remote repository to download the artifacts from. The support library and
  # Google Play service are only distributed there, but third party libraries
  # could use Maven Central or JCenter for example. The default Maven remote
  # is Maven Central.
  _REMOTE_REPO = 'https://maven.google.com'

  # Default Maven repository.
  _DEFAULT_REPO_PATH = os.path.join(
      os.path.expanduser('~'), '.m2', 'repository')

  def __init__(self, debug=False):
    self._repo_path = MavenDownloader._DEFAULT_REPO_PATH
    self._remote_url = MavenDownloader._REMOTE_REPO
    self._debug = debug

  def Install(self, target_repo, artifacts, include_poms=False):
    logging.info('Installing %d artifacts...', len(artifacts))
    downloaders = [_SingleArtifactDownloader(self, artifact, target_repo)
                   for artifact in artifacts]
    if self._debug:
      for downloader in downloaders:
        downloader.Run(include_poms)
    else:
      parallelizer.SyncParallelizer(downloaders).Run(include_poms)
    logging.info('%d artifacts installed to %s', len(artifacts), target_repo)

  @property
  def repo_path(self):
    return self._repo_path

  @property
  def remote_url(self):
    return self._remote_url

  @property
  def debug(self):
    return self._debug


class _SingleArtifactDownloader:
  '''Handles downloading and installing a single Maven artifact.'''

  _POM_FILE_TYPE = 'pom'

  def __init__(self, download_manager, artifact, target_repo):
    self._download_manager = download_manager
    self._artifact = artifact
    self._target_repo = target_repo

  def Run(self, include_pom=False):
    parts = self._artifact.split(':')
    if len(parts) != 4:
      raise Exception('Artifacts expected as '
                      '"group_id:artifact_id:version:file_type".')
    group_id, artifact_id, version, file_type = parts
    self._InstallArtifact(group_id, artifact_id, version, file_type)

    if include_pom and file_type != _SingleArtifactDownloader._POM_FILE_TYPE:
      self._InstallArtifact(group_id, artifact_id, version,
                            _SingleArtifactDownloader._POM_FILE_TYPE)

  def _InstallArtifact(self, group_id, artifact_id, version, file_type):
    logging.debug('Processing %s', self._artifact)

    download_relpath = self._DownloadArtifact(
        group_id, artifact_id, version, file_type)
    logging.debug('Downloaded.')

    install_path = self._ImportArtifact(download_relpath)
    logging.debug('Installed %s', os.path.relpath(install_path))

  def _DownloadArtifact(self, group_id, artifact_id, version, file_type):
    '''
    Downloads the specified artifact using maven, to its standard location, see
    MavenDownloader._DEFAULT_REPO_PATH.
    '''
    cmd = ['mvn',
           'org.apache.maven.plugins:maven-dependency-plugin:RELEASE:get',
           '-DremoteRepositories={}'.format(self._download_manager.remote_url),
           '-Dartifact={}:{}:{}:{}'.format(group_id, artifact_id, version,
                                           file_type)]

    stdout = None if self._download_manager.debug else open(os.devnull, 'wb')

    try:
      ret_code = cmd_helper.Call(cmd, stdout=stdout)
      if ret_code != 0:
        raise Exception('Command "{}" failed'.format(' '.join(cmd)))
    except OSError as e:
      if e.errno == errno.ENOENT:
        raise Exception('mvn command not found. Please install Maven.') from e
      raise

    return os.path.join(os.path.join(*group_id.split('.')),
                        artifact_id,
                        version,
                        '{}-{}.{}'.format(artifact_id, version, file_type))

  def _ImportArtifact(self, artifact_path):
    src_dir = os.path.join(self._download_manager.repo_path, artifact_path)
    dst_dir = os.path.join(self._target_repo, os.path.dirname(artifact_path))

    _MakeDirsIfAbsent(dst_dir)
    shutil.copy(src_dir, dst_dir)

    return dst_dir