| # 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 rules for bootstraping Bazel. |
| |
| |
| This is a quick and dirty rule to make Bazel compile itself. It's not production |
| ready. |
| """ |
| |
| load("@rules_java//java/common:java_common.bzl", "java_common") |
| |
| _JarsInfo = provider(fields = ["compile_time_jars", "runtime_jars"]) |
| |
| def _join_paths(separator, files): |
| """Joins the paths of the files separated by the given separator. |
| |
| Args: |
| separator: string to separate the files paths |
| files: list of files to join their paths |
| """ |
| return separator.join([f.path for f in files]) |
| |
| 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 = depset( |
| transitive = [compile_time_jars, dep[_JarsInfo].compile_time_jars], |
| ) |
| runtime_jars = depset( |
| transitive = [runtime_jars, dep[_JarsInfo].runtime_jars], |
| ) |
| |
| jars = ctx.files.jars |
| neverlink_jars = ctx.files.neverlink_jars |
| compile_time_jars = depset(jars + neverlink_jars, transitive = [compile_time_jars]) |
| runtime_jars = depset(jars, transitive = [runtime_jars]) |
| compile_time_jars_list = compile_time_jars.to_list() # 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.actions.declare_file(class_jar.basename + "-2.params") |
| ctx.actions.write( |
| output = sources_param_file, |
| content = _join_paths("\n", sources), |
| is_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 |
| java_runtime = ctx.attr._jdk[java_common.JavaRuntimeInfo] |
| jar_path = "%s/bin/jar" % java_runtime.java_home |
| |
| if ctx.files.srcjars: |
| files += " @" + javalist_output |
| for file in ctx.files.srcjars: |
| cmd += "%s tf %s | grep '\\.java$' | sed 's|^|%s/|' >> %s\n" % (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 += "%s/bin/javac" % java_runtime.java_home |
| cmd += " " + " ".join(javac_options) |
| if compile_time_jars: |
| cmd += " -classpath '" + _join_paths(ctx.configuration.host_path_separator, compile_time_jars_list) + "'" |
| 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 += (jar_path + " cf " + class_jar.path + " -C " + build_output + " .\n" + |
| "touch " + build_output + "\n") |
| ctx.actions.run_shell( |
| inputs = (sources + compile_time_jars_list + [sources_param_file] + |
| 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) |
| |
| compile_time_jars = depset(transitive = [compile_time_jars], direct = [class_jar]) |
| runtime_jars = depset(transitive = [runtime_jars], direct = [class_jar]) |
| |
| return [ |
| DefaultInfo( |
| files = depset([class_jar]), |
| runfiles = runfiles, |
| ), |
| _JarsInfo( |
| compile_time_jars = compile_time_jars, |
| runtime_jars = runtime_jars, |
| ), |
| ] |
| |
| 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 |
| java_runtime = ctx.attr._jdk[java_common.JavaRuntimeInfo] |
| jar_path = "%s/bin/jar" % java_runtime.java_home |
| ctx.actions.write( |
| output = manifest, |
| content = "Main-Class: " + main_class + "\n", |
| is_executable = False, |
| ) |
| |
| # Cleaning build output directory |
| cmd = "set -e;rm -rf " + build_output + ";mkdir " + build_output + "\n" |
| for jar in library_result[1].runtime_jars.to_list(): |
| cmd += "unzip -qn " + jar.path + " -d " + build_output + "\n" |
| cmd += (jar_path + " cmf " + manifest.path + " " + |
| deploy_jar.path + " -C " + build_output + " .\n" + |
| "touch " + build_output + "\n") |
| |
| ctx.actions.run_shell( |
| inputs = library_result[1].runtime_jars.to_list() + [manifest] + ctx.files._jdk, |
| outputs = [deploy_jar], |
| mnemonic = "Deployjar", |
| command = cmd, |
| use_default_shell_env = True, |
| ) |
| |
| # Write the wrapper. |
| executable = ctx.outputs.executable |
| ctx.actions.write( |
| 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.attr._jdk[java_common.JavaRuntimeInfo].java_executable_exec_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)), |
| "", |
| ]), |
| is_executable = True, |
| ) |
| |
| runfiles = ctx.runfiles(files = [deploy_jar, executable] + ctx.files._jdk, collect_data = True) |
| files_to_build = depset( |
| transitive = [library_result[0].files], |
| direct = [deploy_jar, manifest, executable], |
| ) |
| |
| return [DefaultInfo(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(ctx.files.jars) |
| neverlink_jars = depset(ctx.files.neverlink_jars) |
| runfiles = ctx.runfiles(collect_data = True) |
| compile_time_jars = depset(transitive = [jars, neverlink_jars]) |
| |
| return [ |
| DefaultInfo(files = jars, runfiles = runfiles), |
| _JarsInfo( |
| compile_time_jars = compile_time_jars, |
| runtime_jars = jars, |
| ), |
| ] |
| |
| java_library_attrs = { |
| "_jdk": attr.label( |
| default = Label("@bazel_tools//tools/jdk:current_java_runtime"), |
| providers = [java_common.JavaRuntimeInfo], |
| cfg = "exec", |
| ), |
| "data": attr.label_list(allow_files = True), |
| "resources": attr.label_list(allow_files = True), |
| "srcs": attr.label_list(allow_files = [".java"]), |
| "jars": attr.label_list(allow_files = [".jar"]), |
| "neverlink_jars": attr.label_list(allow_files = [".jar"]), |
| "srcjars": attr.label_list(allow_files = [".jar", ".srcjar"]), |
| "deps": attr.label_list( |
| allow_files = False, |
| providers = [_JarsInfo], |
| ), |
| } |
| |
| 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("@bazel_tools//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(list(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"]), |
| "srcjar": attr.label(allow_files = [".jar", ".srcjar"]), |
| "neverlink_jars": attr.label_list(allow_files = [".jar"], default = []), |
| }, |
| ) |