blob: 488d526cbddf6428c854012b81b1a9e0268d9f3e [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.
"""Rules for supporting the Scala language."""
_scala_filetype = FileType([".scala"])
def _adjust_resources_path(path):
dir_1, dir_2, rel_path = path.partition("resources")
if rel_path:
return dir_1 + dir_2, rel_path
(dir_1,dir_2,rel_path) = path.partition("java")
if rel_path:
return dir_1 + dir_2, rel_path
return "", path
def _compile(ctx, jars, buildijar):
res_cmd = ""
for f in ctx.files.resources:
c_dir, res_path = _adjust_resources_path(f.path)
change_dir = "-C " + c_dir if c_dir else ""
res_cmd = "\n{jar} uf {out} " + change_dir + " " + res_path
ijar_cmd = ""
if buildijar:
ijar_cmd = "\n{ijar} {out} {ijar_out}".format(
ijar=ctx.file._ijar.path,
out=ctx.outputs.jar.path,
ijar_out=ctx.outputs.ijar.path)
cmd = """
set -e
mkdir -p {out}_tmp
{scalac} {scala_opts} {jvm_flags} -classpath "{jars}" $@ -d {out}_tmp
# Make jar file deterministic by setting the timestamp of files
find {out}_tmp -exec touch -t 198001010000 {{}} \;
touch -t 198001010000 {manifest}
{jar} cmf {manifest} {out} -C {out}_tmp .
""" + ijar_cmd + res_cmd
cmd = cmd.format(
scalac=ctx.file._scalac.path,
scala_opts=" ".join(ctx.attr.scalacopts),
jvm_flags=" ".join(["-J" + flag for flag in ctx.attr.jvm_flags]),
out=ctx.outputs.jar.path,
manifest=ctx.outputs.manifest.path,
jar=ctx.file._jar.path,
ijar=ctx.file._ijar.path,
jars=":".join([j.path for j in jars]),)
outs = [ctx.outputs.jar]
if buildijar:
outs.extend([ctx.outputs.ijar])
ctx.action(
inputs=list(jars) +
ctx.files.srcs +
ctx.files.resources +
ctx.files._jdk +
ctx.files._scalasdk +
[ctx.outputs.manifest, ctx.file._jar, ctx.file._ijar],
outputs=outs,
command=cmd,
progress_message="scala %s" % ctx.label,
arguments=[f.path for f in ctx.files.srcs])
def _write_manifest(ctx):
# TODO(bazel-team): I don't think this classpath is what you want
manifest = "Class-Path: %s\n" % ctx.file._scalalib.path
if getattr(ctx.attr, "main_class", ""):
manifest += "Main-Class: %s\n" % ctx.attr.main_class
ctx.file_action(
output = ctx.outputs.manifest,
content = manifest)
def _write_launcher(ctx, jars):
content = """#!/bin/bash
cd $0.runfiles
java -cp {cp} {name} "$@"
"""
content = content.format(
name=ctx.attr.main_class,
deploy_jar=ctx.outputs.jar.path,
cp=":".join([j.short_path for j in jars]))
ctx.file_action(
output=ctx.outputs.executable,
content=content)
def _collect_comp_run_jars(ctx):
compile_jars = set()
runtime_jars = set()
for target in ctx.attr.deps:
if hasattr(target, "runtime_jar_files"):
runtime_jars += target.runtime_jar_files
if hasattr(target, "interface_jar_files"):
compile_jars += target.interface_jar_files
if hasattr(target, "java"):
runtime_jars += target.java.transitive_runtime_deps
#see JavaSkylarkApiProvider.java, this is just the compile-time deps
compile_jars += target.java.transitive_deps
return (compile_jars, runtime_jars)
def _scala_library_impl(ctx):
(cjars, rjars) = _collect_comp_run_jars(ctx)
_write_manifest(ctx)
_compile(ctx, cjars, True)
cjars += [ctx.outputs.ijar]
rjars += [ctx.outputs.jar]
runfiles = ctx.runfiles(
files = list(rjars),
collect_data = True)
return struct(
runtime_jar_files=rjars,
interface_jar_files=cjars,
runfiles=runfiles)
def _scala_macro_library_impl(ctx):
(cjars, rjars) = _collect_comp_run_jars(ctx)
_write_manifest(ctx)
_compile(ctx, cjars, False)
rjars += [ctx.outputs.jar]
# macro code needs to be available at compiletime
cjars += [ctx.outputs.jar]
runfiles = ctx.runfiles(
files = list(rjars),
collect_data = True)
return struct(
runtime_jar_files=rjars,
interface_jar_files=cjars,
runfiles=runfiles)
def _scala_binary_impl(ctx):
(cjars, rjars) = _collect_comp_run_jars(ctx)
_write_manifest(ctx)
_compile(ctx, cjars, False)
rjars += [ctx.outputs.jar, ctx.file._scalalib]
_write_launcher(ctx, rjars)
runfiles = ctx.runfiles(
files = list(rjars) + [ctx.outputs.executable],
collect_data = True)
return struct(
files=set([ctx.outputs.executable]),
runfiles=runfiles)
_implicit_deps = {
"_ijar": attr.label(executable=True, default=Label("//tools/defaults:ijar"), single_file=True, allow_files=True),
"_scalac": attr.label(executable=True, default=Label("@scala//:bin/scalac"), single_file=True, allow_files=True),
"_scalalib": attr.label(default=Label("@scala//:lib/scala-library.jar"), single_file=True, allow_files=True),
"_scalasdk": attr.label(default=Label("@scala//:sdk"), allow_files=True),
"_jar": attr.label(executable=True, default=Label("@bazel_tools//tools/jdk:jar"), single_file=True, allow_files=True),
"_jdk": attr.label(default=Label("//tools/defaults:jdk"), allow_files=True),
}
scala_library = rule(
implementation=_scala_library_impl,
attrs={
"main_class": attr.string(),
"srcs": attr.label_list(
allow_files=_scala_filetype,
non_empty=True),
"deps": attr.label_list(),
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
"resources": attr.label_list(allow_files=True),
"scalacopts": attr.string_list(),
"jvm_flags": attr.string_list(),
} + _implicit_deps,
outputs={
"jar": "%{name}_deploy.jar",
"ijar": "%{name}_ijar.jar",
"manifest": "%{name}_MANIFEST.MF",
},
)
scala_macro_library = rule(
implementation=_scala_macro_library_impl,
attrs={
"main_class": attr.string(),
"srcs": attr.label_list(
allow_files=_scala_filetype,
non_empty=True),
"deps": attr.label_list(),
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
"resources": attr.label_list(allow_files=True),
"scalacopts": attr.string_list(),
"jvm_flags": attr.string_list(),
"_scala-reflect": attr.label(default=Label("@scala//:lib/scala-reflect.jar"), single_file=True, allow_files=True),
} + _implicit_deps,
outputs={
"jar": "%{name}_deploy.jar",
"manifest": "%{name}_MANIFEST.MF",
},
)
scala_binary = rule(
implementation=_scala_binary_impl,
attrs={
"main_class": attr.string(mandatory=True),
"srcs": attr.label_list(
allow_files=_scala_filetype,
non_empty=True),
"deps": attr.label_list(),
"data": attr.label_list(allow_files=True, cfg=DATA_CFG),
"resources": attr.label_list(allow_files=True),
"scalacopts":attr.string_list(),
"jvm_flags": attr.string_list(),
} + _implicit_deps,
outputs={
"jar": "%{name}_deploy.jar",
"manifest": "%{name}_MANIFEST.MF",
},
executable=True,
)