| # Copyright 2014 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_filetype = FileType([".java"]) | |
| jar_filetype = FileType([".jar"]) | |
| srcjar_filetype = FileType([".jar", ".srcjar"]) | |
| # This is a quick and dirty rule to make Bazel compile itself. It's not | |
| # production ready. | |
| def java_library_impl(ctx): | |
| javac_options = ctx.fragments.java.default_javac_flags | |
| class_jar = ctx.outputs.class_jar | |
| compile_time_jars = depset(order = "topological") | |
| runtime_jars = depset(order = "topological") | |
| for dep in ctx.attr.deps: | |
| compile_time_jars += dep.compile_time_jars | |
| runtime_jars += dep.runtime_jars | |
| jars = jar_filetype.filter(ctx.files.jars) | |
| neverlink_jars = jar_filetype.filter(ctx.files.neverlink_jars) | |
| compile_time_jars += jars + neverlink_jars | |
| runtime_jars += jars | |
| compile_time_jars_list = list(compile_time_jars) # TODO: This is weird. | |
| build_output = class_jar.path + ".build_output" | |
| java_output = class_jar.path + ".build_java" | |
| javalist_output = class_jar.path + ".build_java_list" | |
| sources = ctx.files.srcs | |
| sources_param_file = ctx.new_file(ctx.bin_dir, class_jar, "-2.params") | |
| ctx.file_action( | |
| output = sources_param_file, | |
| content = cmd_helper.join_paths("\n", depset(sources)), | |
| executable = False, | |
| ) | |
| # Cleaning build output directory | |
| cmd = "set -e;rm -rf " + build_output + " " + java_output + " " + javalist_output + "\n" | |
| cmd += "mkdir " + build_output + " " + java_output + "\n" | |
| files = " @" + sources_param_file.path | |
| if ctx.files.srcjars: | |
| files += " @" + javalist_output | |
| for file in ctx.files.srcjars: | |
| cmd += "%s tf %s | grep '\.java$' | sed 's|^|%s/|' >> %s\n" % (ctx.file._jar.path, file.path, java_output, javalist_output) | |
| cmd += "unzip %s -d %s >/dev/null\n" % (file.path, java_output) | |
| if ctx.files.srcs or ctx.files.srcjars: | |
| cmd += ctx.file._javac.path | |
| cmd += " " + " ".join(javac_options) | |
| if compile_time_jars: | |
| cmd += " -classpath '" + cmd_helper.join_paths(ctx.configuration.host_path_separator, compile_time_jars) + "'" | |
| cmd += " -d " + build_output + files + "\n" | |
| # We haven't got a good story for where these should end up, so | |
| # stick them in the root of the jar. | |
| for r in ctx.files.resources: | |
| cmd += "cp %s %s\n" % (r.path, build_output) | |
| cmd += (ctx.file._jar.path + " cf " + class_jar.path + " -C " + build_output + " .\n" + | |
| "touch " + build_output + "\n") | |
| ctx.action( | |
| inputs = (sources + compile_time_jars_list + [sources_param_file] + | |
| [ctx.file._jar] + ctx.files._jdk + ctx.files.resources + ctx.files.srcjars), | |
| outputs = [class_jar], | |
| mnemonic = "JavacBootstrap", | |
| command = cmd, | |
| use_default_shell_env = True, | |
| ) | |
| runfiles = ctx.runfiles(collect_data = True) | |
| return struct( | |
| files = depset([class_jar]), | |
| compile_time_jars = compile_time_jars + [class_jar], | |
| runtime_jars = runtime_jars + [class_jar], | |
| runfiles = runfiles, | |
| ) | |
| def java_binary_impl(ctx): | |
| library_result = java_library_impl(ctx) | |
| deploy_jar = ctx.outputs.deploy_jar | |
| manifest = ctx.outputs.manifest | |
| build_output = deploy_jar.path + ".build_output" | |
| main_class = ctx.attr.main_class | |
| ctx.file_action( | |
| output = manifest, | |
| content = "Main-Class: " + main_class + "\n", | |
| executable = False, | |
| ) | |
| # Cleaning build output directory | |
| cmd = "set -e;rm -rf " + build_output + ";mkdir " + build_output + "\n" | |
| for jar in library_result.runtime_jars: | |
| cmd += "unzip -qn " + jar.path + " -d " + build_output + "\n" | |
| cmd += (ctx.file._jar.path + " cmf " + manifest.path + " " + | |
| deploy_jar.path + " -C " + build_output + " .\n" + | |
| "touch " + build_output + "\n") | |
| ctx.action( | |
| inputs = list(library_result.runtime_jars) + [manifest] + ctx.files._jdk, | |
| outputs = [deploy_jar], | |
| mnemonic = "Deployjar", | |
| command = cmd, | |
| use_default_shell_env = True, | |
| ) | |
| # Write the wrapper. | |
| executable = ctx.outputs.executable | |
| ctx.file_action( | |
| output = executable, | |
| content = "\n".join([ | |
| "#!/bin/bash", | |
| "# autogenerated - do not edit.", | |
| "case \"$0\" in", | |
| "/*) self=\"$0\" ;;", | |
| "*) self=\"$PWD/$0\";;", | |
| "esac", | |
| "", | |
| "if [[ -z \"$JAVA_RUNFILES\" ]]; then", | |
| " if [[ -e \"${self}.runfiles\" ]]; then", | |
| " export JAVA_RUNFILES=\"${self}.runfiles\"", | |
| " fi", | |
| " if [[ -n \"$JAVA_RUNFILES\" ]]; then", | |
| " export TEST_SRCDIR=${TEST_SRCDIR:-$JAVA_RUNFILES}", | |
| " fi", | |
| "fi", | |
| "", | |
| "jvm_bin=%s" % (ctx.file._java.path), | |
| "if [[ ! -x ${jvm_bin} ]]; then", | |
| " jvm_bin=$(which java)", | |
| "fi", | |
| # We extract the .so into a temp dir. If only we could mmap | |
| # directly from the zip file. | |
| "DEPLOY=$(dirname $self)/$(basename %s)" % deploy_jar.path, | |
| # This works both on Darwin and Linux, with the darwin path | |
| # looking like tmp.XXXXXXXX.{random} | |
| "SO_DIR=$(mktemp -d -t tmp.XXXXXXXXX)", | |
| "function cleanup() {", | |
| " rm -rf ${SO_DIR}", | |
| "}", | |
| "trap cleanup EXIT", | |
| "unzip -q -d ${SO_DIR} ${DEPLOY} \"*.so\" \"*.dll\" \"*.dylib\" >& /dev/null", | |
| ("${jvm_bin} -Djava.library.path=${SO_DIR} %s -jar $DEPLOY \"$@\"" % | |
| " ".join(ctx.attr.jvm_flags)), | |
| "", | |
| ]), | |
| executable = True, | |
| ) | |
| runfiles = ctx.runfiles(files = [deploy_jar, executable] + ctx.files._jdk, collect_data = True) | |
| files_to_build = depset([deploy_jar, manifest, executable]) | |
| files_to_build += library_result.files | |
| return struct(files = files_to_build, runfiles = runfiles) | |
| def java_import_impl(ctx): | |
| # TODO(bazel-team): Why do we need to filter here? The attribute | |
| # already says only jars are allowed. | |
| jars = depset(jar_filetype.filter(ctx.files.jars)) | |
| neverlink_jars = depset(jar_filetype.filter(ctx.files.neverlink_jars)) | |
| runfiles = ctx.runfiles(collect_data = True) | |
| return struct( | |
| files = jars, | |
| compile_time_jars = jars + neverlink_jars, | |
| runtime_jars = jars, | |
| runfiles = runfiles, | |
| ) | |
| java_library_attrs = { | |
| "_java": attr.label(default = Label("//tools/jdk:java"), single_file = True), | |
| "_javac": attr.label(default = Label("//tools/jdk:javac"), single_file = True), | |
| "_jar": attr.label(default = Label("//tools/jdk:jar"), single_file = True), | |
| "_jdk": attr.label(default = Label("//tools/jdk:jdk"), allow_files = True), | |
| "data": attr.label_list(allow_files = True, cfg = "data"), | |
| "resources": attr.label_list(allow_files = True), | |
| "srcs": attr.label_list(allow_files = java_filetype), | |
| "jars": attr.label_list(allow_files = jar_filetype), | |
| "neverlink_jars": attr.label_list(allow_files = jar_filetype), | |
| "srcjars": attr.label_list(allow_files = srcjar_filetype), | |
| "deps": attr.label_list( | |
| allow_files = False, | |
| providers = ["compile_time_jars", "runtime_jars"], | |
| ), | |
| } | |
| java_library = rule( | |
| java_library_impl, | |
| attrs = java_library_attrs, | |
| outputs = { | |
| "class_jar": "lib%{name}.jar", | |
| }, | |
| fragments = ["java", "cpp"], | |
| ) | |
| # A copy to avoid conflict with native rule. | |
| bootstrap_java_library = rule( | |
| java_library_impl, | |
| attrs = java_library_attrs, | |
| outputs = { | |
| "class_jar": "lib%{name}.jar", | |
| }, | |
| fragments = ["java"], | |
| ) | |
| java_binary_attrs_common = dict(java_library_attrs) | |
| java_binary_attrs_common.update({ | |
| "jvm_flags": attr.string_list(), | |
| "jvm": attr.label(default = Label("//tools/jdk:jdk"), allow_files = True), | |
| }) | |
| java_binary_attrs = dict(java_binary_attrs_common) | |
| java_binary_attrs["main_class"] = attr.string(mandatory = True) | |
| java_binary_outputs = { | |
| "class_jar": "lib%{name}.jar", | |
| "deploy_jar": "%{name}_deploy.jar", | |
| "manifest": "%{name}_MANIFEST.MF", | |
| } | |
| java_binary = rule( | |
| java_binary_impl, | |
| executable = True, | |
| attrs = java_binary_attrs, | |
| outputs = java_binary_outputs, | |
| fragments = ["java", "cpp"], | |
| ) | |
| # A copy to avoid conflict with native rule | |
| bootstrap_java_binary = rule( | |
| java_binary_impl, | |
| executable = True, | |
| attrs = java_binary_attrs, | |
| outputs = java_binary_outputs, | |
| fragments = ["java"], | |
| ) | |
| java_test = rule( | |
| java_binary_impl, | |
| executable = True, | |
| attrs = dict(java_binary_attrs_common.items() + [ | |
| ("main_class", attr.string(default = "org.junit.runner.JUnitCore")), | |
| # TODO(bazel-team): it would be better if we could offer a | |
| # test_class attribute, but the "args" attribute is hard | |
| # coded in the bazel infrastructure. | |
| ]), | |
| outputs = java_binary_outputs, | |
| test = True, | |
| fragments = ["java", "cpp"], | |
| ) | |
| java_import = rule( | |
| java_import_impl, | |
| attrs = { | |
| "jars": attr.label_list(allow_files = jar_filetype), | |
| "srcjar": attr.label(allow_files = srcjar_filetype), | |
| "neverlink_jars": attr.label_list(allow_files = jar_filetype, default = []), | |
| }, | |
| ) |