| # 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 |
| ) |