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