blob: 90047eb0ae3fabe20a9b38ac9a288d71b1033227 [file]
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "parse_netrc")
load("//private/lib:coordinates.bzl", "unpack_coordinates")
#
# Parsing tools
#
def _parse_exclusion_spec_list(exclusion_specs):
"""
Given a string (g:a), returns an exclusion map
"""
exclusions = []
for exclusion_spec in exclusion_specs:
if type(exclusion_spec) == "string":
pieces = exclusion_spec.split(":")
if len(pieces) == 2:
exclusion_spec = {"group": pieces[0], "artifact": pieces[1]}
else:
fail(("Invalid exclusion: %s. Exclusions are specified as " +
"group-id:artifact-id, without the version, packaging or " +
"classifier.") % exclusion_spec)
exclusions.append(exclusion_spec)
return exclusions
def _parse_maven_coordinate_string(mvn_coord):
"""
Given a string containing a standard Maven coordinate (g:a:[p:[c:]]v) or gradle external dependency (g:a:v:c@p), returns a maven artifact map (see above).
"""
unpacked = unpack_coordinates(mvn_coord)
# It would be nice to use `bazel_skylib//lib:structs.bzl` for this, but we avoid
# loading it here to keep this file lightweight and free of external dependencies.
return {
key: getattr(unpacked, key)
for key in dir(unpacked)
if key != "to_json" and key != "to_proto"
}
def _parse_repository_spec_list(repository_specs):
"""
Given a list containing either strings or repository maps (see above), returns a list containing repository maps.
"""
repos = []
for repo in repository_specs:
if type(repo) == "string":
repos.append({"repo_url": repo})
else:
repos.append(repo)
return repos
def _parse_artifact_spec_list(artifact_specs):
"""
Given a list containing either strings or artifact maps (see above), returns a list containing artifact maps.
"""
artifacts = []
for artifact in artifact_specs:
if type(artifact) == "string":
artifacts.append(_parse_maven_coordinate_string(artifact))
else:
if "version" not in artifact:
artifact["version"] = ""
artifacts.append(artifact)
return artifacts
parse = struct(
parse_maven_coordinate = _parse_maven_coordinate_string,
parse_repository_spec_list = _parse_repository_spec_list,
parse_artifact_spec_list = _parse_artifact_spec_list,
parse_exclusion_spec_list = _parse_exclusion_spec_list,
)
#
# JSON serialization
#
def _repository_credentials_spec_to_json(credentials_spec):
"""
Given a repository credential spec or None, returns the json serialization of the object, or None if the object wasn't given.
"""
if credentials_spec != None:
return "{ \"user\": \"" + credentials_spec["user"] + "\", \"password\": \"" + credentials_spec["password"] + "\" }"
else:
return None
def _repository_spec_to_json(repository_spec):
"""
Given a repository spec, returns the json serialization of the object.
"""
maybe_credentials_json = _repository_credentials_spec_to_json(repository_spec.get("credentials"))
if maybe_credentials_json != None:
return "{ \"repo_url\": \"" + repository_spec["repo_url"] + "\", \"credentials\": " + maybe_credentials_json + " }"
else:
return "{ \"repo_url\": \"" + repository_spec["repo_url"] + "\" }"
def _exclusion_spec_to_json(exclusion_spec):
"""
Given an artifact exclusion spec, returns the json serialization of the object.
"""
return "{ \"group\": \"" + exclusion_spec["group"] + "\", \"artifact\": \"" + exclusion_spec["artifact"] + "\" }"
def _exclusion_spec_list_to_json(exclusion_spec):
"""
Given a list of artifact exclusion spec, returns its json serialization.
"""
return "[" + ", ".join([_exclusion_spec_to_json(spec) for spec in exclusion_spec]) + "]"
def _override_license_types_spec_to_json(override_license_types_spec):
"""
Given an override license types spec, returns the json serialization of the object.
"""
license_type_strings = []
for license_type in override_license_types_spec:
license_type_strings.append("\"" + license_type + "\"")
return ("[" + ", ".join(license_type_strings) + "]")
def _artifact_spec_to_json(artifact_spec):
"""
Given an artifact spec, returns the json serialization of the object.
"""
maybe_exclusion_specs_jsons = []
for spec in _parse_exclusion_spec_list(artifact_spec.get("exclusions") or []):
maybe_exclusion_specs_jsons.append(_exclusion_spec_to_json(spec))
exclusion_specs_json = (("[" + ", ".join(maybe_exclusion_specs_jsons) + "]") if len(maybe_exclusion_specs_jsons) > 0 else None)
required = "{ \"group\": \"" + artifact_spec["group"] + \
"\", \"artifact\": \"" + artifact_spec["artifact"] + \
"\", \"version\": \"" + artifact_spec["version"] + "\""
with_packaging = required + ((", \"packaging\": \"" + artifact_spec["packaging"] + "\"") if artifact_spec.get("packaging") != None else "")
with_classifier = with_packaging + ((", \"classifier\": \"" + artifact_spec["classifier"] + "\"") if artifact_spec.get("classifier") != None else "")
with_override_license_types = with_classifier + ((", " + _override_license_types_spec_to_json(artifact_spec["override_license_types"])) if artifact_spec.get("override_license_types") != None else "")
with_exclusions = with_override_license_types + ((", \"exclusions\": " + exclusion_specs_json) if artifact_spec.get("exclusions") != None else "")
with_neverlink = with_exclusions + ((", \"neverlink\": " + str(artifact_spec.get("neverlink")).lower()) if artifact_spec.get("neverlink") != None else "")
with_testonly = with_neverlink + ((", \"testonly\": " + str(artifact_spec.get("testonly")).lower()) if artifact_spec.get("testonly") != None else "")
with_forced_version = with_testonly + ((", \"force_version\": true") if artifact_spec.get("force_version") != None else "")
return with_forced_version + " }"
json = struct(
write_repository_credentials_spec = _repository_credentials_spec_to_json,
write_repository_spec = _repository_spec_to_json,
write_exclusion_spec = _exclusion_spec_to_json,
write_exclusion_spec_list = _exclusion_spec_list_to_json,
write_override_license_types_spec = _override_license_types_spec_to_json,
write_artifact_spec = _artifact_spec_to_json,
)
#
# Accessors
#
#
# Coursier expects artifacts to be defined in the form `group:artifact:version`, but it also supports some attributes:
# `url`, `ext`, `classifier`, `exclude` and `type`.
# In contrast with group, artifact and version, the attributes are a key=value comma-separated string appended at the end,
# For example: `coursier fetch group:artifact:version,classifier=xxx,url=yyy`
#
def _artifact_to_coord(artifact):
classifier = (",classifier=" + artifact["classifier"]) if artifact.get("classifier") != None else ""
type = (",type=" + artifact["packaging"]) if artifact.get("packaging") not in (None, "jar") else ""
return artifact["group"] + ":" + artifact["artifact"] + ":" + artifact["version"] + classifier + type
#
# Returns a string "{hostname} {user}:{password}" for a repository_spec
#
def _repository_credentials(repository_spec):
if "credentials" not in repository_spec:
fail("Asked to generate credentials for a repository that does not need it")
(_, remainder) = repository_spec["repo_url"].split("//")
hostname = remainder.split("/")[0]
credentials = repository_spec["credentials"]
return _coursier_credential(hostname, credentials["user"], credentials["password"])
def _coursier_credential(hostname, username, password):
"""format a coursier credential
See https://get-coursier.io/docs/other-credentials.html#inline for docs on this format
Args:
hostname: the host string
username: the user string
password: the pass string
Returns:
the credential string
"""
return hostname + " " + username + ":" + password
def _netrc_credentials(netrc_content):
"""return a list of credentials from netrc content
Args:
netrc_content: a Dict<string,Dict<string,string>> from parse_netrc.
Returns:
a List<string> for the coursier cli --credentials option.
"""
credentials = []
for machine, props in parse_netrc(netrc_content).items():
if machine and "login" in props and "password" in props:
credential = _coursier_credential(machine, props["login"], props["password"])
credentials.append(credential)
return credentials
utils = struct(
artifact_coordinate = _artifact_to_coord,
netrc_credentials = _netrc_credentials,
parse_netrc = parse_netrc,
repo_credentials = _repository_credentials,
)