blob: 0691ec28f2cd862963294fd0a807b77c89554e34 [file]
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# War packaging.
load("@rules_java//java:defs.bzl", "JavaInfo")
jar_filetype = [".jar"]
# Special prefix added by rules_jvm_external.jvm_import() to stamped jars
# https://github.com/bazel-contrib/rules_jvm_external/blob/6.9/private/rules/jvm_import.bzl#L32
PROCESSED_PREFIX = "processed_"
def war_jar_name(f):
"""Return the jar file name as it will appear inside the WAR."""
raw = f.basename
if raw.startswith(PROCESSED_PREFIX):
raw = raw[len(PROCESSED_PREFIX):]
sp = f.short_path
# Rename ONLY caffeine's "guava" artifact (not Google Guava)
# Matches: .../com/github/ben-manes/caffeine/guava/<ver>/processed_guava-<ver>.jar
if "/com/github/ben-manes/caffeine/guava/" in sp and raw.startswith("guava-") and raw.endswith(".jar"):
raw = "caffeine-" + raw # -> caffeine-guava-<ver>.jar
# Keep existing Gerrit naming rules
if sp.startswith("gerrit-"):
raw = sp.split("/")[0] + "-" + raw
elif sp.startswith("java/"):
raw = sp[5:].replace("/", "_")
return raw
def normalize_jar_id(jar_name):
"""Version-agnostic jar identity used for allowlists/inventories."""
n = jar_name
if n.endswith(".jar"):
n = n[:-4]
i = n.rfind("-")
# Strip trailing "-<version-ish>" where the suffix begins with a digit.
if i > 0 and n[i + 1:i + 2].isdigit():
n = n[:i]
return n
def should_skip_packaged_jar(ctx, jar_name):
"""jar_name must be the post-processed name (war_jar_name output)."""
for pfx in ctx.attr.exclude_jar_prefixes:
if jar_name.startswith(pfx):
return True
return False
def is_third_party_jar_id(ctx, jar_id):
"""Return True if jar_id should be tracked in third-party allowlists."""
if jar_id in ctx.attr.exclude_third_party_jar:
return False
for pfx in ctx.attr.exclude_third_party_jar_prefix:
if jar_id.startswith(pfx):
return False
return True
def _add_context(in_file, output):
return [
"unzip -qd %s %s" % (output, in_file.path),
]
def _add_jar_file(in_file, output):
raw = war_jar_name(in_file)
output_path = output + raw
return _add_file(in_file.path, output_path)
def _add_web_xml_file(in_file, output):
return _add_file(in_file.path, output + in_file.basename)
def _add_file(in_file, output):
return [
"test -L %s || ln -s $(pwd)/%s %s" % (output, in_file, output),
]
def _make_war(input_dir, output):
return "(%s)" % " && ".join([
"root=$(pwd)",
"cd %s" % input_dir,
"find . -exec touch -t 198001010000 '{}' ';' 2> /dev/null",
"zip -X -9qr ${root}/%s ." % (output.path),
])
def _war_impl(ctx):
war = ctx.outputs.war
build_output = war.path + ".build_output"
inputs = []
# Metadata we expose for checks/tools.
jar_entries = []
jar_ids = []
# Create war layout
cmd = [
"set -e;rm -rf " + build_output,
"mkdir -p " + build_output,
"mkdir -p %s/WEB-INF/lib" % build_output,
"mkdir -p %s/WEB-INF/pgm-lib" % build_output,
]
# Add web.xml files
if ctx.attr.web_xml:
for web_xml in ctx.attr.web_xml.files.to_list():
inputs.append(web_xml)
cmd = cmd + _add_web_xml_file(web_xml, build_output + "/WEB-INF/")
# Add runtime libs
transitive_libs = []
for l in ctx.attr.libs:
if JavaInfo in l:
transitive_libs.append(l[JavaInfo].transitive_runtime_jars)
elif hasattr(l, "files"):
transitive_libs.append(l.files)
for dep in depset(transitive = transitive_libs).to_list():
packaged = war_jar_name(dep)
if should_skip_packaged_jar(ctx, packaged):
continue
cmd += _add_jar_file(dep, build_output + "/WEB-INF/lib/")
inputs.append(dep)
jar_entries.append("WEB-INF/lib/" + packaged)
jid = normalize_jar_id(packaged)
if is_third_party_jar_id(ctx,jid):
jar_ids.append(jid)
# Add pgm libs
transitive_pgmlibs = []
for l in ctx.attr.pgmlibs:
transitive_pgmlibs.append(l[JavaInfo].transitive_runtime_jars)
for dep in depset(transitive = transitive_pgmlibs).to_list():
packaged = war_jar_name(dep)
if should_skip_packaged_jar(ctx, packaged):
continue
if dep not in inputs:
cmd += _add_jar_file(dep, build_output + "/WEB-INF/pgm-lib/")
inputs.append(dep)
jar_entries.append("WEB-INF/pgm-lib/" + packaged)
jid = normalize_jar_id(packaged)
if is_third_party_jar_id(ctx, jid):
jar_ids.append(jid)
# Add context
transitive_context_libs = []
if ctx.attr.context:
for jar in ctx.attr.context:
if JavaInfo in jar:
transitive_context_libs.append(jar[JavaInfo].transitive_runtime_jars)
elif hasattr(jar, "files"):
transitive_context_libs.append(jar.files)
for dep in depset(transitive = transitive_context_libs).to_list():
cmd += _add_context(dep, build_output)
inputs.append(dep)
# Write deterministic manifests for checks.
#
# NOTE: The manifests are produced as independent actions.
# Bazel will only execute the actions needed for the requested output,
# so building *.war.entries.txt does not materialize the WAR.
ctx.actions.write(
output = ctx.outputs.jars,
content = "\n".join(sorted(depset(jar_ids).to_list())) + "\n",
)
ctx.actions.write(
output = ctx.outputs.entries,
content = "\n".join(sorted(jar_entries)) + "\n",
)
# Add zip war
cmd.append(_make_war(build_output, war))
ctx.actions.run_shell(
inputs = inputs,
outputs = [war],
mnemonic = "WAR",
command = "\n".join(cmd),
use_default_shell_env = True,
)
return [
DefaultInfo(files = depset([war, ctx.outputs.jars, ctx.outputs.entries])),
]
# context: go to the root directory
# libs: go to the WEB-INF/lib directory
# pgmlibs: go to the WEB-INF/pgm-lib directory
# web_xml: go to the WEB-INF directory
_pkg_war = rule(
attrs = {
"context": attr.label_list(allow_files = True),
"libs": attr.label_list(allow_files = jar_filetype),
"pgmlibs": attr.label_list(allow_files = False),
"web_xml": attr.label(allow_files = True),
"exclude_jar_prefixes": attr.string_list(),
"exclude_third_party_jar": attr.string_list(),
"exclude_third_party_jar_prefix": attr.string_list(),
},
outputs = {
"war": "%{name}.war",
"jars": "%{name}.war.jars.txt",
"entries": "%{name}.war.entries.txt",
},
implementation = _war_impl,
)
def pkg_war(name, context = [], **kwargs):
_pkg_war(
name = name,
context = context,
**kwargs
)