1# Copyright (C) 2024 The Dagger Authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""See javadoc_library.""" 16 17load("@rules_java//java:defs.bzl", "JavaInfo", "java_common") 18 19def _android_jar(android_api_level): 20 if android_api_level == -1: 21 return None 22 return Label("@androidsdk//:platforms/android-%s/android.jar" % android_api_level) 23 24def _javadoc_library(ctx): 25 transitive_deps = [] 26 for dep in ctx.attr.deps: 27 if JavaInfo in dep: 28 transitive_deps.append(dep[JavaInfo].transitive_compile_time_jars) 29 30 if ctx.attr._android_jar: 31 transitive_deps.append(ctx.attr._android_jar.files) 32 33 classpath = depset([], transitive = transitive_deps).to_list() 34 35 java_home = str(ctx.attr._jdk[java_common.JavaRuntimeInfo].java_home) 36 37 output_dir = ctx.actions.declare_directory("%s_javadoc" % ctx.attr.name) 38 39 javadoc_arguments = ctx.actions.args() 40 javadoc_arguments.use_param_file("@%s", use_always = True) 41 javadoc_arguments.set_param_file_format("multiline") 42 43 javadoc_command = java_home + "/bin/javadoc" 44 45 javadoc_arguments.add("-use") 46 javadoc_arguments.add("-encoding", "UTF8") 47 javadoc_arguments.add_joined("-classpath", classpath, join_with = ":") 48 javadoc_arguments.add("-notimestamp") 49 javadoc_arguments.add("-d", output_dir.path) 50 javadoc_arguments.add("-Xdoclint:-missing") 51 javadoc_arguments.add("-quiet") 52 53 # Documentation for the javadoc command 54 # https://docs.oracle.com/javase/9/javadoc/javadoc-command.htm 55 if ctx.attr.root_packages: 56 # TODO(b/167433657): Reevaluate the utility of root_packages 57 # 1. Find the first directory under the working directory named '*java'. 58 # 2. Assume all files to document can be found by appending a root_package name 59 # to that directory, or a subdirectory, replacing dots with slashes. 60 javadoc_command += ' -sourcepath $(find * -type d -name "*java" -print0 | tr "\\0" :) ' 61 javadoc_arguments.add_all(ctx.attr.root_packages) 62 javadoc_arguments.add_joined("-subpackages", ctx.attr.root_packages, join_with = ":") 63 else: 64 # Document exactly the code in the specified source files. 65 javadoc_arguments.add_all(ctx.files.srcs) 66 67 if ctx.attr.doctitle: 68 javadoc_arguments.add("-doctitle", ctx.attr.doctitle, format = '"%s"') 69 70 if ctx.attr.groups: 71 groups = [] 72 for k, v in ctx.attr.groups.items(): 73 groups.append("-group \"%s\" \"%s\"" % (k, ":".join(v))) 74 javadoc_arguments.add_all(groups) 75 76 javadoc_arguments.add_joined("-exclude", ctx.attr.exclude_packages, join_with = ":") 77 78 javadoc_arguments.add_all( 79 ctx.attr.external_javadoc_links, 80 map_each = _format_linkoffline_value, 81 ) 82 83 if ctx.attr.bottom_text: 84 javadoc_arguments.add("-bottom", ctx.attr.bottom_text, format = '"%s"') 85 86 # TODO(ronshapiro): Should we be using a different tool that doesn't include 87 # timestamp info? 88 jar_command = "%s/bin/jar cf %s -C %s ." % (java_home, ctx.outputs.jar.path, output_dir.path) 89 90 srcs = depset(transitive = [src.files for src in ctx.attr.srcs]).to_list() 91 ctx.actions.run_shell( 92 inputs = srcs + classpath + ctx.files._jdk, 93 command = "%s $@ && %s" % (javadoc_command, jar_command), 94 arguments = [javadoc_arguments], 95 outputs = [output_dir, ctx.outputs.jar], 96 ) 97 98def _format_linkoffline_value(link): 99 return "-linkoffline {0} {0}".format(link) 100 101javadoc_library = rule( 102 attrs = { 103 "srcs": attr.label_list( 104 allow_empty = False, 105 allow_files = True, 106 doc = "Source files to generate Javadoc for.", 107 ), 108 "deps": attr.label_list( 109 doc = """ 110Targets that contain references to other types referenced in Javadoc. These can 111be the java_library/android_library target(s) for the same sources. 112""", 113 ), 114 "doctitle": attr.string( 115 default = "", 116 doc = "Title for generated index.html. See javadoc -doctitle.", 117 ), 118 "groups": attr.string_list_dict( 119 doc = "Groups specified packages together in overview page. See javadoc -groups.", 120 ), 121 "root_packages": attr.string_list( 122 doc = """ 123Java packages to include in generated Javadoc. Any subpackages not listed in 124exclude_packages will be included as well. If none are provided, each file in 125`srcs` is processed. 126""", 127 ), 128 "exclude_packages": attr.string_list( 129 doc = "Java packages to exclude from generated Javadoc.", 130 ), 131 "android_api_level": attr.int( 132 default = -1, 133 doc = """ 134If Android APIs are used, the API level to compile against to generate Javadoc. 135""", 136 ), 137 "bottom_text": attr.string( 138 default = "", 139 doc = "Text passed to Javadoc's `-bottom` flag.", 140 ), 141 "external_javadoc_links": attr.string_list( 142 doc = "URLs passed to Javadoc's `-linkoffline` flag.", 143 ), 144 "_android_jar": attr.label( 145 default = _android_jar, 146 allow_single_file = True, 147 ), 148 "_jdk": attr.label( 149 default = Label("@bazel_tools//tools/jdk:current_java_runtime"), 150 providers = [java_common.JavaRuntimeInfo], 151 ), 152 }, 153 outputs = {"jar": "%{name}.jar"}, 154 doc = "Generates a Javadoc jar path/to/target/<name>.jar.", 155 implementation = _javadoc_library, 156) 157