xref: /aosp_15_r20/external/angle/build/3pp_common/maven.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1# Copyright 2024 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""The fetch.py and install.py helpers for a 3pp Maven module."""
5
6import os
7import pathlib
8import re
9import shutil
10import subprocess
11import urllib.request
12
13import common
14
15APACHE_MAVEN_URL = 'https://repo.maven.apache.org/maven2'
16
17_POM_TEMPLATE = """\
18<project>
19  <modelVersion>4.0.0</modelVersion>
20  <groupId>group</groupId>
21  <artifactId>artifact</artifactId>
22  <version>1</version>
23  <dependencies>
24    <dependency>
25      <groupId>{group_id}</groupId>
26      <artifactId>{artifact_id}</artifactId>
27      <version>{version}</version>
28      <scope>runtime</scope>
29    </dependency>
30  </dependencies>
31  <build>
32    <plugins>
33      <plugin>
34        <artifactId>maven-assembly-plugin</artifactId>
35        <version>3.3.0</version>
36        <configuration>
37          <descriptorRefs>
38            <descriptorRef>jar-with-dependencies</descriptorRef>
39          </descriptorRefs>
40        </configuration>
41        <executions>
42          <execution>
43            <phase>package</phase>
44            <goals>
45              <goal>single</goal>
46            </goals>
47          </execution>
48        </executions>
49      </plugin>
50    </plugins>
51  </build>
52  <repositories>
53    <repository>
54      <id>placeholder_id</id>
55      <name>placeholder_name</name>
56      <url>{maven_url}</url>
57    </repository>
58  </repositories>
59</project>
60"""
61
62
63def _detect_latest(maven_url, package):
64    metadata_url = '{}/{}/maven-metadata.xml'.format(
65        maven_url,
66        package.replace('.', '/').replace(':', '/'))
67    metadata = urllib.request.urlopen(metadata_url).read().decode('utf-8')
68    # Do not parse xml with the Python included parser since it is susceptible
69    # to maliciously crafted xmls. Only use regular expression parsing to be
70    # safe. RE should be enough to handle what we need to extract.
71    # TODO(agrieve): Use 'if m := ..." once docker image updates from Python 3.6.
72    m = re.search('<latest>([^<]+)</latest>', metadata)
73    if m:
74        latest = m.group(1)
75    else:
76        # If no latest info was found just hope the versions are sorted and the
77        # last one is the latest (as is commonly the case).
78        latest = re.findall('<version>([^<]+)</version>', metadata)[-1]
79    return latest
80
81
82def _install(output_prefix,
83             deps_prefix,
84             maven_url,
85             package,
86             version,
87             jar_name=None,
88             post_process_func=None):
89    # Runs in a docker container.
90    group_id, artifact_id = package.split(':')
91    if not jar_name:
92        jar_name = f'{artifact_id}.jar'
93
94    pathlib.Path('pom.xml').write_text(
95        _POM_TEMPLATE.format(version=version,
96                             group_id=group_id,
97                             artifact_id=artifact_id,
98                             maven_url=maven_url))
99
100    # Set up JAVA_HOME for the mvn command to find the JDK.
101    env = os.environ.copy()
102    env['JAVA_HOME'] = common.path_within_checkout('third_party/jdk/current')
103
104    # Ensure that mvn works and the environment is set up correctly.
105    subprocess.run(['mvn', '-v'], check=True, env=env)
106
107    # Build the jar file, explicitly specify -f to reduce sources of error.
108    subprocess.run(['mvn', 'clean', 'assembly:single', '-f', 'pom.xml'],
109                   check=True,
110                   env=env)
111
112    src_jar_path = 'target/artifact-1-jar-with-dependencies.jar'
113    dst_jar_path = os.path.join(output_prefix, jar_name)
114    if post_process_func:
115        post_process_func(src_jar_path, dst_jar_path)
116    else:
117        shutil.move(src_jar_path, dst_jar_path)
118
119
120def main(*,
121         package,
122         jar_name=None,
123         maven_url='https://dl.google.com/android/maven2',
124         post_process_func=None,
125         version_override=None):
126    """3pp entry point for fetch.py.
127
128    Args:
129      package: E.g.: some.package:some-thing
130      jar_name: Name of .jar. Defaults to |some-thing|.jar
131      maven_url: URL of Maven repository.
132      post_process_func: Called to finish. Args: src_jar_path, dst_jar_path
133      version_override: Use this version instead of the latest one.
134    """
135
136    def do_latest():
137        return version_override or _detect_latest(maven_url, package)
138
139    def do_install(args):
140        _install(args.output_prefix, args.deps_prefix, maven_url, package,
141                 args.version, jar_name, post_process_func)
142
143    common.main(do_latest=do_latest,
144                do_install=do_install,
145                runtime_deps=['//third_party/jdk/current'])
146