xref: /aosp_15_r20/external/cronet/build/android/pylib/utils/maven_downloader.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env vpython3
2# Copyright 2017 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import errno
7import logging
8import os
9import shutil
10import sys
11
12sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
13import devil_chromium  # pylint: disable=unused-import
14from devil.utils import cmd_helper
15from devil.utils import parallelizer
16
17
18def _MakeDirsIfAbsent(path):
19  try:
20    os.makedirs(path)
21  except OSError as err:
22    if err.errno != errno.EEXIST or not os.path.isdir(path):
23      raise
24
25
26class MavenDownloader:
27  '''
28  Downloads and installs the requested artifacts from the Google Maven repo.
29  The artifacts are expected to be specified in the format
30  "group_id:artifact_id:version:file_type", as the default file type is JAR
31  but most Android libraries are provided as AARs, which would otherwise fail
32  downloading. See Install()
33  '''
34
35  # Remote repository to download the artifacts from. The support library and
36  # Google Play service are only distributed there, but third party libraries
37  # could use Maven Central or JCenter for example. The default Maven remote
38  # is Maven Central.
39  _REMOTE_REPO = 'https://maven.google.com'
40
41  # Default Maven repository.
42  _DEFAULT_REPO_PATH = os.path.join(
43      os.path.expanduser('~'), '.m2', 'repository')
44
45  def __init__(self, debug=False):
46    self._repo_path = MavenDownloader._DEFAULT_REPO_PATH
47    self._remote_url = MavenDownloader._REMOTE_REPO
48    self._debug = debug
49
50  def Install(self, target_repo, artifacts, include_poms=False):
51    logging.info('Installing %d artifacts...', len(artifacts))
52    downloaders = [_SingleArtifactDownloader(self, artifact, target_repo)
53                   for artifact in artifacts]
54    if self._debug:
55      for downloader in downloaders:
56        downloader.Run(include_poms)
57    else:
58      parallelizer.SyncParallelizer(downloaders).Run(include_poms)
59    logging.info('%d artifacts installed to %s', len(artifacts), target_repo)
60
61  @property
62  def repo_path(self):
63    return self._repo_path
64
65  @property
66  def remote_url(self):
67    return self._remote_url
68
69  @property
70  def debug(self):
71    return self._debug
72
73
74class _SingleArtifactDownloader:
75  '''Handles downloading and installing a single Maven artifact.'''
76
77  _POM_FILE_TYPE = 'pom'
78
79  def __init__(self, download_manager, artifact, target_repo):
80    self._download_manager = download_manager
81    self._artifact = artifact
82    self._target_repo = target_repo
83
84  def Run(self, include_pom=False):
85    parts = self._artifact.split(':')
86    if len(parts) != 4:
87      raise Exception('Artifacts expected as '
88                      '"group_id:artifact_id:version:file_type".')
89    group_id, artifact_id, version, file_type = parts
90    self._InstallArtifact(group_id, artifact_id, version, file_type)
91
92    if include_pom and file_type != _SingleArtifactDownloader._POM_FILE_TYPE:
93      self._InstallArtifact(group_id, artifact_id, version,
94                            _SingleArtifactDownloader._POM_FILE_TYPE)
95
96  def _InstallArtifact(self, group_id, artifact_id, version, file_type):
97    logging.debug('Processing %s', self._artifact)
98
99    download_relpath = self._DownloadArtifact(
100        group_id, artifact_id, version, file_type)
101    logging.debug('Downloaded.')
102
103    install_path = self._ImportArtifact(download_relpath)
104    logging.debug('Installed %s', os.path.relpath(install_path))
105
106  def _DownloadArtifact(self, group_id, artifact_id, version, file_type):
107    '''
108    Downloads the specified artifact using maven, to its standard location, see
109    MavenDownloader._DEFAULT_REPO_PATH.
110    '''
111    cmd = ['mvn',
112           'org.apache.maven.plugins:maven-dependency-plugin:RELEASE:get',
113           '-DremoteRepositories={}'.format(self._download_manager.remote_url),
114           '-Dartifact={}:{}:{}:{}'.format(group_id, artifact_id, version,
115                                           file_type)]
116
117    stdout = None if self._download_manager.debug else open(os.devnull, 'wb')
118
119    try:
120      ret_code = cmd_helper.Call(cmd, stdout=stdout)
121      if ret_code != 0:
122        raise Exception('Command "{}" failed'.format(' '.join(cmd)))
123    except OSError as e:
124      if e.errno == errno.ENOENT:
125        raise Exception('mvn command not found. Please install Maven.') from e
126      raise
127
128    return os.path.join(os.path.join(*group_id.split('.')),
129                        artifact_id,
130                        version,
131                        '{}-{}.{}'.format(artifact_id, version, file_type))
132
133  def _ImportArtifact(self, artifact_path):
134    src_dir = os.path.join(self._download_manager.repo_path, artifact_path)
135    dst_dir = os.path.join(self._target_repo, os.path.dirname(artifact_path))
136
137    _MakeDirsIfAbsent(dst_dir)
138    shutil.copy(src_dir, dst_dir)
139
140    return dst_dir
141