blob: 4e5e9bf2c3d4108f30fe87b9d64a8d95ae4dca98 [file] [log] [blame]
# Copyright 2015 The Bazel Authors. All rights reserved.
#
# 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.
"""Java AppEngine support for Bazel.
For now, it only support bundling a WebApp and running locally.
To create a WebApp for Google AppEngine, add the rules:
appengine_war(
name = "MyWebApp",
# Jars to use for the classpath in the webapp.
jars = ["//java/com/google/examples/mywebapp:java"],
# data to put in the webapp, the directory structure of the data set
# will be maintained.
data = ["//java/com/google/examples/mywebapp:data"],
# Data's root path, it will be considered as the root of the data files.
# If unspecified, the path to the current package will be used. The path is
# relative to current package or, relative to the workspace root if starting
# with a leading slash.
data_path = "/java/com/google/examples/mywebapp",
)
You can also make directly a single target for it with:
java_war(
name = "MyWebApp",
srcs = glob(["**/*.java"]),
resources = ["..."],
data = ["..."],
data_path = "...",
)
Resources will be put in the classpath whereas data will be bundled at the root
of the war file. This is strictly equivalent to (it is actually a convenience
macros that translate to that):
java_library(
name = "libMyWebApp",
srcs = glob(["**/*.java"]),
resources = ["..."],
)
appengine_war(
name = "MyWebApp",
jars = [":libMyWebApp"],
data = ["..."],
data_path = "...",
)
This rule will also create a .deploy target that will try to use the AppEngine
SDK to upload your application to AppEngine. It takes an optional argument: the
APP_ID. If not specified, it uses the default APP_ID provided in the application
web.xml.
"""
jar_filetype = FileType([".jar"])
def _add_file(in_file, output, path = None):
output_path = output
input_path = in_file.path
if path and input_path.startswith(path):
output_path += input_path[len(path):]
return [
"mkdir -p $(dirname %s)" % output_path,
"test -L %s || ln -s $(pwd)/%s %s\n" % (output_path, input_path,
output_path)
]
def _make_war(zipper, input_dir, output):
return [
"(root=$(pwd);" +
("cd %s &&" % input_dir) +
("${root}/%s Cc ${root}/%s $(find .))" % (zipper.path, output.path))
]
def _common_substring(str1, str2):
i = 0
res = ""
for c in str1:
if str2[i] != c:
return res
res += c
i += 1
return res
def _short_path_dirname(path):
sp = path.short_path
return sp[0:len(sp)-len(path.basename)-1]
def _war_impl(ctxt):
zipper = ctxt.file._zipper
data_path = ctxt.attr.data_path
if not data_path:
data_path = _short_path_dirname(ctxt.outputs.war)
elif data_path[0] == "/":
data_path = data_path[1:]
else: # relative path
data_path = _short_path_dirname(ctxt.outputs.war) + "/" + data_path
war = ctxt.outputs.war
build_output = war.path + ".build_output"
cmd = [
"set -e;rm -rf " + build_output,
"mkdir -p " + build_output
]
inputs = ctxt.files.jars + [zipper]
cmd += ["mkdir -p %s" % build_output + "/WEB-INF/lib"]
for jar in ctxt.files.jars:
# Add the jar to WEB-INF/lib.
cmd += _add_file(jar, build_output + "/WEB-INF/lib")
# Add its runtime classpath to WEB-INF/lib
if hasattr(jar, "java"):
inputs += jar.java.transitive_runtime_deps
for run_jar in jar.java.transitive_runtime_deps:
cmd += _add_file(run_jar, build_output + "/WEB-INF/lib")
for jar in ctxt.files._appengine_deps:
cmd += _add_file(jar, build_output + "/WEB-INF/lib")
inputs += [jar]
inputs += ctxt.files.data
for res in ctxt.files.data:
# Add the data file
cmd += _add_file(res, build_output, path = data_path)
cmd += _make_war(zipper, build_output, war)
ctxt.action(
inputs = inputs,
outputs = [war],
mnemonic="WAR",
command="\n".join(cmd),
use_default_shell_env=True)
executable = ctxt.outputs.executable
appengine_sdk = None
for f in ctxt.files._appengine_sdk:
if not appengine_sdk:
appengine_sdk = f.path
elif not f.path.startswith(appengine_sdk):
appengine_sdk = _common_substring(appengine_sdk, f.path)
classpath = [
"${JAVA_RUNFILES}/%s" % jar.short_path
for jar in ctxt.files._appengine_jars
]
substitutions = {
"%{zipper}": ctxt.file._zipper.short_path,
"%{war}": ctxt.outputs.war.short_path,
"%{java}": ctxt.file._java.short_path,
"%{appengine_sdk}": appengine_sdk,
"%{classpath}": (":".join(classpath)),
}
ctxt.template_action(
output = executable,
template = ctxt.file._runner_template,
substitutions = substitutions,
executable = True)
ctxt.template_action(
output = ctxt.outputs.deploy,
template = ctxt.file._deploy_template,
substitutions = substitutions,
executable = True)
runfiles = ctxt.runfiles(files = [war, executable]
+ ctxt.files._appengine_sdk
+ ctxt.files._appengine_jars
+ [ctxt.file._java, ctxt.file._zipper])
return struct(runfiles = runfiles)
appengine_war = rule(
_war_impl,
executable = True,
attrs = {
"_java": attr.label(
default=Label("//tools/jdk:java"),
single_file=True),
"_zipper": attr.label(
default=Label("//third_party/ijar:zipper"),
single_file=True),
"_runner_template": attr.label(
default=Label("//tools/build_rules/appengine:runner_template"),
single_file=True),
"_deploy_template": attr.label(
default=Label("//tools/build_rules/appengine:deploy_template"),
single_file=True),
"_appengine_sdk": attr.label(
default=Label("//external:appengine/java/sdk")),
"_appengine_jars": attr.label(
default=Label("//external:appengine/java/jars")),
"_appengine_deps": attr.label_list(
default=[
Label("@appengine-java//:api"),
Label("@commons-lang//jar"),
Label("//third_party:apache_commons_collections"),
]
),
"jars": attr.label_list(allow_files=jar_filetype, mandatory=True),
"data": attr.label_list(allow_files=True),
"data_path": attr.string(),
},
outputs = {
"war": "%{name}.war",
"deploy": "%{name}.deploy",
})
def java_war(name, data=[], data_path=None, **kwargs):
native.java_library(name = "lib%s" % name, **kwargs)
appengine_war(name = name,
jars = ["lib%s" % name],
data=data,
data_path=data_path)