Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

155 lines (143 sloc) 6.11 KB
"""Contains maven_jars build rule, which is a higher-level alternative to maven_jar.
Effectively this allows listing out a set of high-level requirements and resolving
the transitive dependencies from Maven Central. It's more akin to how one would specify
dependencies in e.g. Gradle, but does not offer the reprodicibility guarantees that
maven_jar would.
"""
def maven_jars(name:str, id:str='', ids:list=[], repository:str|list=None, exclude:list=[],
hashes:list=None, combine:bool=False, hash:str|list=None, deps:list=[],
visibility:list=None, filename:str=None, deps_only:bool=False, optional:list=None):
"""Fetches a transitive set of dependencies from Maven.
Args:
name (str): Name of the output rule.
id (str): Maven id of the artifact (e.g. org.junit:junit:4.1.0)
ids (list): Maven ids of artifacts to fetch (e.g. org.junit:junit:4.1.0, io.grpc:grpc-all:1.4.0)
repository (str | list): Maven repositories to fetch deps from.
exclude (list): Dependencies to ignore when fetching this one.
hashes (dict): Map of Maven id -> rule hash for each rule produced.
combine (bool): If True, we combine all downloaded .jar files into one uberjar.
hash (str | list): Hash of final produced .jar. For brevity, implies combine=True.
deps (list): Labels of dependencies, as usual.
visibility (list): Visibility label.
filename (str): Filename we attempt to download. Defaults to standard Maven name.
deps_only (bool): If True we fetch only dependent rules, not this one itself. Useful for some that
have a top-level target as a facade which doesn't have actual code.
optional (list): List of optional dependencies to fetch. By default we fetch none of them.
"""
if id:
ids.append(id)
for id in ids:
if id.count(':') != 2:
raise ValueError('Bad Maven id string: %s. Must be in the format group:artifact:id' % id)
combine = combine or hash
source_name = f'_{name}#src'
repository = repository or CONFIG.DEFAULT_MAVEN_REPO
repos = [repository] if isinstance(repository, str) else repository
def get_hash(id, artifact=None):
if hashes is None:
return None
artifact = artifact or id.split(':')[1]
return hashes.get(id, hashes.get(artifact, '<not given>'))
def create_maven_deps(_, output):
for line in output:
if not line:
continue
group, artifact, version, sources, licences = _parse_maven_artifact(line)
if artifact in exclude:
continue
maven_jar(
name=artifact,
id=line,
repository=repos,
hash=get_hash(id, artifact),
licences=licences,
sources=sources,
# We deliberately don't make this rule visible externally.
)
# Have to account for require/provide on final rule if it's a jar
final_name = name if deps_only or combine else f'_{name}#bin'
add_exported_dep(final_name, ':' + artifact)
if combine:
add_exported_dep(source_name, ':' + artifact)
exclusions = ' '.join(['-e ' + excl for excl in exclude])
options = ' '.join(['-o ' + option for option in optional]) if optional else ''
repo_flags = ' '.join(['-r ' + repo for repo in repos])
build_rule(
name='_%s#deps' % name,
cmd='$TOOL %s %s %s %s' % (repo_flags, ' '.join(ids), exclusions, options),
post_build=create_maven_deps,
building_description='Finding dependencies...',
tools=[CONFIG.MAVEN_TOOL],
sandbox=False,
)
if combine:
download_name = f'_{name}#download'
maven_jar(
name=download_name,
id=id,
repository=repos,
hash=get_hash(id),
deps = deps,
visibility=visibility,
filename=filename,
)
# Combine the sources into a separate uberjar
cmd, tools = _jarcat_cmd()
build_rule(
name=source_name,
output_is_complete=True,
needs_transitive_deps=True,
building_description="Creating source jar...",
deps=[':' + download_name, f':_{name}#deps'] + deps,
outs=[name + '_src.jar'],
cmd=cmd + ' -s src.jar -e ""',
tools=tools,
)
return build_rule(
name=name,
hashes=hash if isinstance(hash, list) else [hash] if hash else None,
output_is_complete=True,
needs_transitive_deps=True,
building_description="Creating jar...",
deps=[':' + download_name, ':' + source_name, f':_{name}#deps'] + deps,
outs=[name + '.jar'],
requires=['java'],
visibility=visibility,
cmd=cmd + ' -e "_src.jar"',
tools=tools,
)
elif not deps_only:
return maven_jar(
name=name,
id=id,
repository=repos,
hash=get_hash(id),
deps = deps + [f':_{name}#deps'],
visibility=visibility,
filename=filename,
)
else:
return build_rule(
name=name,
deps=[f':_{name}#deps'],
exported_deps=deps,
cmd='true', # do nothing!
visibility=visibility,
requires=['java'],
)
def _parse_maven_artifact(id, sources=True, licences=None):
"""Parses a Maven artifact in group:artifact:version format, with possibly some extras."""
parts = id.split(':')
if len(parts) == 5:
group, artifact, version = parts[:3]
sources = parts[3] == 'src'
licences = parts[4].split('|')
elif len(parts) == 4:
group, artifact, version = parts[:3]
sources = parts[3] == 'src'
elif len(parts) == 3:
group, artifact, version = parts
else:
raise ParseError(f'Unknown artifact format: {id} (should be group:artifact:version)')
return group, artifact, version, sources, licences
CONFIG.setdefault('MAVEN_TOOL', '//java/maven')
You can’t perform that action at this time.