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