From a24098abaf06245614aeb64540eede97ffcd2dd4 Mon Sep 17 00:00:00 2001 From: Vincent Rose Date: Thu, 5 Dec 2024 15:59:56 -0700 Subject: [PATCH 1/2] Allow package exclusions and inclusions in javadocs --- private/rules/java_export.bzl | 14 ++ private/rules/javadoc.bzl | 23 +++ private/rules/kt_jvm_export.bzl | 13 +- .../rules_jvm_external/javadoc/BUILD | 38 +++- .../rules_jvm_external/javadoc/CreateJar.java | 87 ++++++++ .../javadoc/JavadocJarMaker.java | 181 ++++++++++++----- .../rules_jvm_external/javadoc/BUILD | 19 +- .../javadoc/ExclusionTest.java | 186 ++++++++++++++++++ 8 files changed, 499 insertions(+), 62 deletions(-) create mode 100644 private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/CreateJar.java create mode 100644 tests/com/github/bazelbuild/rules_jvm_external/javadoc/ExclusionTest.java diff --git a/private/rules/java_export.bzl b/private/rules/java_export.bzl index 94a433a1d..b49c5dd80 100644 --- a/private/rules/java_export.bzl +++ b/private/rules/java_export.bzl @@ -88,6 +88,8 @@ def java_export( doc_deps = kwargs.pop("doc_deps", []) doc_url = kwargs.pop("doc_url", "") doc_resources = kwargs.pop("doc_resources", []) + doc_excluded_packages = kwargs.pop("doc_excluded_packages", []) + doc_included_packages = kwargs.pop("doc_included_packages", []) toolchains = kwargs.pop("toolchains", []) # Construct the java_library we'll export from here. @@ -115,6 +117,8 @@ def java_export( doc_deps = doc_deps, doc_url = doc_url, doc_resources = doc_resources, + doc_excluded_packages = doc_excluded_packages, + doc_included_packages = doc_included_packages, toolchains = toolchains, ) @@ -135,6 +139,8 @@ def maven_export( doc_deps = [], doc_url = "", doc_resources = [], + doc_excluded_packages = [], + doc_included_packages = [], toolchains = None): """ All arguments are the same as java_export with the addition of: @@ -194,6 +200,12 @@ def maven_export( doc_url: The URL at which the generated `javadoc` will be hosted (if not using `tags = ["no-javadoc"]`). doc_resources: Resources to be included in the javadoc jar. + doc_excluded_packages: A list of packages to exclude from the generated javadoc. Wildcards are supported at the + end of the package name. For example, `com.example.*` will exclude all the subpackages of `com.example`, while + `com.example` will exclude only the files directly in `com.example` + doc_included_packages: A list of packages to include in the generated javadoc. Wildcards are supported at the + end of the package name. For example, `com.example.*` will include all the subpackages of `com.example`, while + `com.example` will include only the files directly in `com.example` visibility: The visibility of the target kwargs: These are passed to [`java_library`](https://bazel.build/reference/be/java#java_library), and so may contain any valid parameter for that rule. @@ -264,6 +276,8 @@ def maven_export( doc_deps = doc_deps, doc_url = doc_url, doc_resources = doc_resources, + excluded_packages = doc_excluded_packages, + included_packages = doc_included_packages, excluded_workspaces = excluded_workspaces.keys(), additional_dependencies = additional_dependencies, visibility = visibility, diff --git a/private/rules/javadoc.bzl b/private/rules/javadoc.bzl index f3c391c1b..cad7629ea 100644 --- a/private/rules/javadoc.bzl +++ b/private/rules/javadoc.bzl @@ -25,20 +25,28 @@ def generate_javadoc( javadocopts, doc_deps, doc_resources, + excluded_packages, output, element_list): inputs = [] transitive_inputs = [] args = ctx.actions.args() + args.add("--out", output) args.add("--element-list", element_list) + args.add_all(source_jars, before_each = "--in") inputs.extend(source_jars) + args.add_all(ctx.files.doc_resources, before_each = "--resources") inputs.extend(ctx.files.doc_resources) + args.add_all(classpath, before_each = "--cp") transitive_inputs.append(classpath) + args.add_all(excluded_packages, before_each = "--exclude-packages") + args.add_all(ctx.attr.included_packages, before_each = "--include-packages") + for dep in doc_deps: dep_info = dep[_JavadocInfo] args.add("-linkoffline") @@ -90,6 +98,7 @@ def _javadoc_impl(ctx): ctx.attr.javadocopts, ctx.attr.doc_deps, ctx.attr.doc_resources, + ctx.attr.excluded_packages, jar_file, element_list, ) @@ -153,6 +162,20 @@ javadoc = rule( allow_files = True, default = [], ), + "excluded_packages": attr.string_list( + doc = """A list of packages to exclude from the generated javadoc. Wildcards are supported at the end of + the package name. For example, `com.example.*` will exclude all the subpackages of `com.example`, while + `com.example` will exclude only the files directly in `com.example`.""", + allow_empty = True, + default = [], + ), + "included_packages": attr.string_list( + doc = """A list of packages to include in the generated javadoc. Wildcards are supported at the end of + the package name. For example, `com.example.*` will include all the subpackages of `com.example`, while + `com.example` will include only the files directly in `com.example`.""", + allow_empty = True, + default = [], + ), "excluded_workspaces": attr.string_list( doc = "A list of bazel workspace names to exclude from the generated jar", allow_empty = True, diff --git a/private/rules/kt_jvm_export.bzl b/private/rules/kt_jvm_export.bzl index 70b9d6bbb..59fa0075a 100644 --- a/private/rules/kt_jvm_export.bzl +++ b/private/rules/kt_jvm_export.bzl @@ -2,7 +2,7 @@ load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") load(":java_export.bzl", "maven_export") load(":maven_project_jar.bzl", "DEFAULT_EXCLUDED_WORKSPACES") -KOTLIN_STDLIB = Label("@rules_kotlin//kotlin/compiler:kotlin-stdlib") +KOTLIN_STDLIB = "rules_kotlin" def kt_jvm_export( name, @@ -70,11 +70,8 @@ def kt_jvm_export( javadocopts = kwargs.pop("javadocopts", None) - # ensure that the kotlin-stdlib is included in deploy_env - if KOTLIN_STDLIB not in deploy_env: - updated_deploy_env = deploy_env + [KOTLIN_STDLIB] - else: - updated_deploy_env = deploy_env + updated_excluded_workspaces = {name: None for name in excluded_workspaces} + updated_excluded_workspaces.update({KOTLIN_STDLIB: None}) # Construct the kt_jvm_library we'll export from here. kt_jvm_library( @@ -88,8 +85,8 @@ def kt_jvm_export( name = name, maven_coordinates = maven_coordinates, lib_name = lib_name, - deploy_env = updated_deploy_env, - excluded_workspaces = excluded_workspaces, + deploy_env = deploy_env, + excluded_workspaces = updated_excluded_workspaces, pom_template = pom_template, visibility = visibility, tags = tags, diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD b/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD index 1f006d035..77afd7762 100644 --- a/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD @@ -1,14 +1,48 @@ -load("@rules_java//java:defs.bzl", "java_binary") +load("@rules_java//java:defs.bzl", "java_binary", "java_library") + +java_library( + name = "javadoc_lib", + srcs = ["JavadocJarMaker.java"], + visibility = [ + "//tests/com/github/bazelbuild/rules_jvm_external/javadoc:__pkg__", + ], + deps = [ + ":create_jar_lib", + "//private/tools/java/com/github/bazelbuild/rules_jvm_external", + "//private/tools/java/com/github/bazelbuild/rules_jvm_external/zip", + ], +) java_binary( name = "javadoc", - srcs = glob(["*.java"]), main_class = "com.github.bazelbuild.rules_jvm_external.javadoc.JavadocJarMaker", visibility = [ "//visibility:public", ], + runtime_deps = [ + ":javadoc_lib", + ], +) + +java_library( + name = "create_jar_lib", + srcs = ["CreateJar.java"], + visibility = [ + "//tests/com/github/bazelbuild/rules_jvm_external/javadoc:__pkg__", + ], deps = [ "//private/tools/java/com/github/bazelbuild/rules_jvm_external", "//private/tools/java/com/github/bazelbuild/rules_jvm_external/zip", ], ) + +java_binary( + name = "create_jar", + main_class = "com.github.bazelbuild.rules_jvm_external.javadoc.CreateJar", + visibility = [ + "//visibility:public", + ], + runtime_deps = [ + ":create_jar_lib", + ], +) diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/CreateJar.java b/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/CreateJar.java new file mode 100644 index 000000000..25a31107e --- /dev/null +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/CreateJar.java @@ -0,0 +1,87 @@ +package com.github.bazelbuild.rules_jvm_external.javadoc; + +import com.github.bazelbuild.rules_jvm_external.zip.StableZipEntry; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Comparator; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class CreateJar { + + public static void main(String[] args) throws IOException { + Path out = Paths.get(args[0]); + Set inputs = Stream.of(args).skip(1).map(Paths::get).collect(Collectors.toSet()); + + Path tmpDir = Files.createTempDirectory("create-jar-temp"); + tmpDir.toFile().deleteOnExit(); + + for (Path input : inputs) { + if (!Files.isDirectory(input)) { + Files.copy(input, tmpDir.resolve(input.getFileName()), StandardCopyOption.REPLACE_EXISTING); + continue; + } + + Files.walk(input) + .forEachOrdered( + source -> { + try { + Path target = tmpDir.resolve(input.relativize(source)); + if (Files.isDirectory(source)) { + Files.createDirectories(target); + } else { + Files.createDirectories(target.getParent()); + Files.copy(source, target); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + createJar(out, tmpDir); + } + + public static void createJar(Path out, Path inputDir) throws IOException { + try (OutputStream os = Files.newOutputStream(out); + ZipOutputStream zos = new ZipOutputStream(os); + Stream walk = Files.walk(inputDir)) { + + walk.sorted(Comparator.naturalOrder()) + .forEachOrdered( + path -> { + if (path.equals(inputDir)) { + return; + } + + try { + if (Files.isDirectory(path)) { + String name = inputDir.relativize(path) + "/"; + ZipEntry entry = new StableZipEntry(name); + zos.putNextEntry(entry); + zos.closeEntry(); + } else { + String name = inputDir.relativize(path).toString(); + ZipEntry entry = new StableZipEntry(name); + zos.putNextEntry(entry); + try (InputStream is = Files.newInputStream(path)) { + com.github.bazelbuild.rules_jvm_external.ByteStreams.copy(is, zos); + } + zos.closeEntry(); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } +} diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/JavadocJarMaker.java b/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/JavadocJarMaker.java index bcf9136f4..b883c8c7d 100644 --- a/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/JavadocJarMaker.java +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc/JavadocJarMaker.java @@ -21,12 +21,12 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.github.bazelbuild.rules_jvm_external.ByteStreams; -import com.github.bazelbuild.rules_jvm_external.zip.StableZipEntry; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; +import java.io.Reader; import java.io.StringWriter; import java.io.UncheckedIOException; import java.io.Writer; @@ -37,12 +37,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -63,8 +64,9 @@ public static void main(String[] args) throws IOException { Path out = null; Path elementList = null; Set classpath = new HashSet<>(); + Set excludedPackages = new HashSet<>(); + Set includedPackages = new HashSet<>(); List options = new ArrayList<>(); - for (int i = 0; i < args.length; i++) { String flag = args[i]; String next; @@ -95,6 +97,16 @@ public static void main(String[] args) throws IOException { resources.add(Paths.get(next)); break; + case "--exclude-packages": + next = args[++i]; + excludedPackages.add(next); + break; + + case "--include-packages": + next = args[++i]; + includedPackages.add(next); + break; + default: options.add(flag); break; @@ -119,18 +131,20 @@ public static void main(String[] args) throws IOException { try (StandardJavaFileManager fileManager = tool.getStandardFileManager(null, Locale.getDefault(), UTF_8)) { fileManager.setLocation( - DocumentationTool.Location.DOCUMENTATION_OUTPUT, Arrays.asList(dir.toFile())); + DocumentationTool.Location.DOCUMENTATION_OUTPUT, List.of(dir.toFile())); fileManager.setLocation( StandardLocation.CLASS_PATH, classpath.stream().map(Path::toFile).collect(Collectors.toSet())); - Set sources = new HashSet<>(); - Set topLevelPackages = new HashSet<>(); - Path unpackTo = Files.createTempDirectory("unpacked-sources"); tempDirs.add(unpackTo); - Set fileNames = new HashSet<>(); - readSourceFiles(unpackTo, fileManager, sourceJars, sources, topLevelPackages, fileNames); + Map> sources = new HashMap<>(); + readSourceFiles(unpackTo, fileManager, sourceJars, sources); + Set expandedExcludedPackages = expandPackages(excludedPackages, sources.keySet()); + Set expandedIncludedPackages = expandPackages(includedPackages, sources.keySet()); + filterPackages(unpackTo, sources, expandedIncludedPackages, expandedExcludedPackages); + Set topLevelPackages = + sources.keySet().stream().map(p -> p.split("\\.")[0]).collect(Collectors.toSet()); // True if we're just exporting a set of modules if (sources.isEmpty()) { @@ -177,7 +191,32 @@ public static void main(String[] args) throws IOException { options.addAll(Arrays.asList("-d", outputTo.toAbsolutePath().toString())); - sources.forEach(obj -> options.add(obj.getName())); + // sourcepath and subpackages should work in most cases. A known edge case is when the package + // names + // don't match the directory structure. For example `Main.java` in + // `tests/integration/java_export` has + // a package of "com.jvm.external.jvm_export" but the file is in + // `tests/integration/java_export/Main.java`. + // The error comes from the javadoc tool itself. It seems that `-subpackage` looks at the + // directory structure, + // not the package name in the file. For this reason, include/exclude will not work when the + // package name + // doesn't match the directory structure. + if (!expandedExcludedPackages.isEmpty()) { + options.add("-sourcepath"); + options.add(unpackTo.toAbsolutePath().toString()); + + options.add("-subpackages"); + options.add(String.join(":", topLevelPackages)); + + // It might appear that -exclude is not needed since we + // remove the source files, but without it + // empty package info html files will still be generated. + options.add("-exclude"); + options.add(String.join(":", expandedExcludedPackages)); + } + + sources.values().stream().flatMap(List::stream).forEach(s -> options.add(s.getName())); for (Path resource : resources) { Path target = outputTo.resolve(resource.getFileName()); @@ -187,7 +226,7 @@ public static void main(String[] args) throws IOException { Writer writer = new StringWriter(); DocumentationTool.DocumentationTask task = - tool.getTask(writer, fileManager, null, null, options, sources); + tool.getTask(writer, fileManager, null, null, options, null); Boolean result = task.call(); if (result == null || !result) { System.err.println("javadoc " + String.join(" ", options)); @@ -203,37 +242,7 @@ public static void main(String[] args) throws IOException { Files.createFile(generatedElementList); } - try (OutputStream os = Files.newOutputStream(out); - ZipOutputStream zos = new ZipOutputStream(os); - Stream walk = Files.walk(outputTo)) { - - walk.sorted(Comparator.naturalOrder()) - .forEachOrdered( - path -> { - if (path.equals(outputTo)) { - return; - } - - try { - if (Files.isDirectory(path)) { - String name = outputTo.relativize(path) + "/"; - ZipEntry entry = new StableZipEntry(name); - zos.putNextEntry(entry); - zos.closeEntry(); - } else { - String name = outputTo.relativize(path).toString(); - ZipEntry entry = new StableZipEntry(name); - zos.putNextEntry(entry); - try (InputStream is = Files.newInputStream(path)) { - ByteStreams.copy(is, zos); - } - zos.closeEntry(); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - } + CreateJar.createJar(out, outputTo); } tempDirs.forEach(JavadocJarMaker::delete); } @@ -253,9 +262,7 @@ private static void readSourceFiles( Path unpackTo, StandardJavaFileManager fileManager, Set sourceJars, - Set sources, - Set topLevelPackages, - Set fileNames) + Map> sources) throws IOException { for (Path jar : sourceJars) { @@ -280,16 +287,88 @@ private static void readSourceFiles( ByteStreams.copy(zis, out); } - fileManager.getJavaFileObjects(target.toFile()).forEach(sources::add); + fileManager + .getJavaFileObjects(target.toFile()) + .forEach( + s -> { + String p = extractPackageName(s); + sources.computeIfAbsent(p, k -> new ArrayList<>()).add(s); + }); + } + } + } + } + + // If the package ends in .* , then look for all subpackages in packages set + private static Set expandPackages(Set wildcardedPackages, Set packages) { + Set expandedPackages = new HashSet<>(); - String[] segments = name.split("/"); - if (segments.length > 0 && !"META-INF".equals(segments[0])) { - topLevelPackages.add(segments[0]); + for (String excludedPackage : wildcardedPackages) { + if (excludedPackage.endsWith(".*")) { + String basePackage = excludedPackage.substring(0, excludedPackage.length() - 2); + for (String pkg : packages) { + if (pkg.startsWith(basePackage)) { + expandedPackages.add(pkg); } + } + } else { + expandedPackages.add(excludedPackage); + } + } + + return expandedPackages; + } - fileNames.add(name); + // Extract the package name from the contents of the file + private static String extractPackageName(JavaFileObject fileObject) { + Set keywords = Set.of("public", "class", "interface", "enum"); + try (Reader reader = fileObject.openReader(true); + BufferedReader bufferedReader = new BufferedReader(reader)) { + String line; + while ((line = bufferedReader.readLine()) != null) { + line = line.trim(); + if (line.startsWith("package ")) { + return line.substring("package ".length(), line.indexOf(';')).trim(); + } + + // Stop looking if we hit the class or interface declaration + if (keywords.stream().anyMatch(line::startsWith)) { + break; } } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + // default package + return ""; + } + + private static void filterPackages( + Path unpackTo, + Map> sources, + Set expandedIncludedPackages, + Set expandedExcludedPackages) { + // If no "include" packages are specified, then include everything + // minus the excluded packages. + // If "include" packages are specified, then only include those packages + // AND subtract the excluded packages. + if (!expandedIncludedPackages.isEmpty()) { + sources.keySet().retainAll(expandedIncludedPackages); + } + + for (String excludedPackage : expandedExcludedPackages) { + sources + .getOrDefault(excludedPackage, new ArrayList<>()) + .forEach( + s -> { + try { + Files.deleteIfExists(unpackTo.resolve(s.getName())); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + sources.remove(excludedPackage); } } } diff --git a/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD b/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD index 1d41b9d92..05447c6e6 100644 --- a/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD +++ b/tests/com/github/bazelbuild/rules_jvm_external/javadoc/BUILD @@ -6,7 +6,24 @@ java_test( test_class = "com.github.bazelbuild.rules_jvm_external.javadoc.ResourceTest", deps = [ "//private/tools/java/com/github/bazelbuild/rules_jvm_external", - "//private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc", + "//private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc:javadoc_lib", + "//tests/com/github/bazelbuild/rules_jvm_external:zip_utils", + artifact( + "junit:junit", + repository_name = "regression_testing_coursier", + ), + artifact("com.google.guava:guava"), + ], +) + +java_test( + name = "ExclusionTest", + srcs = ["ExclusionTest.java"], + test_class = "com.github.bazelbuild.rules_jvm_external.javadoc.ExclusionTest", + deps = [ + "//private/tools/java/com/github/bazelbuild/rules_jvm_external", + "//private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc:create_jar_lib", + "//private/tools/java/com/github/bazelbuild/rules_jvm_external/javadoc:javadoc_lib", "//tests/com/github/bazelbuild/rules_jvm_external:zip_utils", artifact( "junit:junit", diff --git a/tests/com/github/bazelbuild/rules_jvm_external/javadoc/ExclusionTest.java b/tests/com/github/bazelbuild/rules_jvm_external/javadoc/ExclusionTest.java new file mode 100644 index 000000000..8b41e9fc0 --- /dev/null +++ b/tests/com/github/bazelbuild/rules_jvm_external/javadoc/ExclusionTest.java @@ -0,0 +1,186 @@ +package com.github.bazelbuild.rules_jvm_external.javadoc; + +import static com.github.bazelbuild.rules_jvm_external.ZipUtils.createJar; +import static com.github.bazelbuild.rules_jvm_external.ZipUtils.readJar; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ExclusionTest { + + private Path inputJar; + private Path outputJar; + private Path elementList; + + @Rule public TemporaryFolder temp = new TemporaryFolder(); + + @Before + public void setUp() throws IOException { + this.inputJar = temp.newFile("in.jar").toPath(); + this.outputJar = temp.newFile("out.jar").toPath(); + this.elementList = temp.newFile("element-list").toPath(); + // deleting the file since JavadocJarMaker fails on existing files, we just need to supply the + // path. + elementList.toFile().delete(); + } + + @Test + public void testJavadocPackageExclusion() throws IOException { + createJar( + inputJar, + ImmutableMap.of( + "com/example/Main.java", + "package com.example; public class Main { public static void main(String[] args) {} }", + "com/example/processor/Processor.java", + "package com.example.processor; public class Processor {}", + "com/example/processor/internal/InternalProcessor.java", + "package com.example.processor.internal; public class InternalProcessor {}", + "com/example/state/internal/InternalThing.java", + "package com.example.state.internal; public class InternalThing {}", + "com/example/state/Thing.java", + "package com.example.state; public class Thing {}", + "com/example/query/Query.java", + "package com.example.query; public class Query {}", + "com/example/query/internal/InternalQuery.java", + "package com.example.query.internal; public class InternalQuery {}")); + + JavadocJarMaker.main( + new String[] { + "--in", + inputJar.toAbsolutePath().toString(), + "--out", + outputJar.toAbsolutePath().toString(), + "--exclude-packages", + "com.example.processor.internal", + "--exclude-packages", + "com.example.state.internal", + "--exclude-packages", + "com.example.query.internal", + "--element-list", + elementList.toAbsolutePath().toString() + }); + + Map contents = readJar(outputJar); + + assertTrue(contents.containsKey("com/example/Main.html")); + + assertTrue(contents.containsKey("com/example/processor/Processor.html")); + assertTrue(!contents.containsKey("com/example/processor/internal/InternalProcessor.html")); + + assertTrue(contents.containsKey("com/example/state/Thing.html")); + assertTrue(!contents.containsKey("com/example/state/internal/InternalThing.html")); + + assertTrue(contents.containsKey("com/example/query/Query.html")); + assertTrue(!contents.containsKey("com/example/query/internal/InternalQuery.html")); + } + + @Test + public void testJavadocPackageExclusionWithAsterisk() throws IOException { + createJar( + inputJar, + ImmutableMap.of( + "com/example/Main.java", + "package com.example; public class Main { public static void main(String[] args) {} }", + "com/example/processor/Processor.java", + "package com.example.processor; public class Processor {}", + "com/example/processor/internal/InternalProcessor.java", + "package com.example.processor.internal; public class InternalProcessor {}", + "com/example/processor/internal/other/OtherProcessor.java", + "package com.example.processor.internal.other; public class OtherProcessor {}")); + + JavadocJarMaker.main( + new String[] { + "--in", + inputJar.toAbsolutePath().toString(), + "--out", + outputJar.toAbsolutePath().toString(), + "--exclude-packages", + "com.example.processor.internal.*", + "--element-list", + elementList.toAbsolutePath().toString() + }); + + Map contents = readJar(outputJar); + + // With asterisk, the "other" subpackage should be excluded as well. + assertTrue(contents.containsKey("com/example/Main.html")); + assertTrue(contents.containsKey("com/example/processor/Processor.html")); + + assertFalse(contents.containsKey("com/example/processor/internal/InternalProcessor.html")); + assertFalse(contents.containsKey("com/example/processor/internal/other/OtherProcessor.html")); + } + + @Test + public void testJavadocPackageToplevelExcluded() throws IOException { + createJar( + inputJar, + ImmutableMap.of( + "com/example/Main.java", + "package com.example; public class Main { public static void main(String[] args) {} }", + "io/example/processor/Processor.java", + "package io.example.processor; public class Processor {}")); + + JavadocJarMaker.main( + new String[] { + "--in", + inputJar.toAbsolutePath().toString(), + "--out", + outputJar.toAbsolutePath().toString(), + "--exclude-packages", + "io.example.*", + "--element-list", + elementList.toAbsolutePath().toString() + }); + + Map contents = readJar(outputJar); + + // Checking that the toplevel package "io" is excluded. If it wasn't, the javadoc command + // would throw an error for -subpackage containing a package that doesn't exist. + assertTrue(contents.containsKey("com/example/Main.html")); + assertFalse(contents.containsKey("io/example/processor/Processor.html")); + } + + @Test + public void testIncludeCombinedWithExclude() throws IOException { + createJar( + inputJar, + ImmutableMap.of( + "com/example/Main.java", + "package com.example; public class Main { public static void main(String[] args) {} }", + "io/example/Thing.java", + "package io.example; public class Thing {}", + "com/example/internal/InternalThing.java", + "package com.example.internal; public class InternalThing {}")); + + JavadocJarMaker.main( + new String[] { + "--in", + inputJar.toAbsolutePath().toString(), + "--out", + outputJar.toAbsolutePath().toString(), + "--exclude-packages", + "com.example.internal", + "--include-packages", + "com.example.*", + "--element-list", + elementList.toAbsolutePath().toString() + }); + + Map contents = readJar(outputJar); + + // The include gets applied before the exclude. + // io.example is not explicitely excluded, but its not in the include list, so it should not + // appear. + assertTrue(contents.containsKey("com/example/Main.html")); + assertFalse(contents.containsKey("io/example/Thing.html")); + assertFalse(contents.containsKey("com/example/internal/InternalThing.html")); + } +} From af2a926970caa70c4e189e053d91262c9fbfc289 Mon Sep 17 00:00:00 2001 From: Vincent Rose Date: Thu, 5 Dec 2024 16:27:17 -0700 Subject: [PATCH 2/2] regenerate docs --- docs/api.md | 6 ++++-- private/rules/java_export.bzl | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/api.md b/docs/api.md index c9a10f5e1..5440fc37d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -18,8 +18,8 @@ load("@rules_jvm_external//:defs.bzl", "maven_install", "artifact")
 load("@rules_jvm_external//:defs.bzl", "javadoc")
 
-javadoc(name, deps, additional_dependencies, doc_deps, doc_resources, doc_url, excluded_workspaces,
-        javadocopts)
+javadoc(name, deps, additional_dependencies, doc_deps, doc_resources, doc_url, excluded_packages,
+        excluded_workspaces, included_packages, javadocopts)
 
Generate a javadoc from all the `deps` @@ -35,7 +35,9 @@ Generate a javadoc from all the `deps` | doc_deps | `javadoc` targets referenced by the current target.

Use this to automatically add appropriate `-linkoffline` javadoc options to resolve references to packages documented by the given javadoc targets that have `url` specified. | List of labels | optional | `[]` | | doc_resources | Resources to include in the javadoc jar. | List of labels | optional | `[]` | | doc_url | The URL at which this documentation will be hosted.

This information is only used by javadoc targets depending on this target. | String | optional | `""` | +| excluded_packages | A list of packages to exclude from the generated javadoc. Wildcards are supported at the end of the package name. For example, `com.example.*` will exclude all the subpackages of `com.example`, while `com.example` will exclude only the files directly in `com.example`. | List of strings | optional | `[]` | | excluded_workspaces | A list of bazel workspace names to exclude from the generated jar | List of strings | optional | `["com_google_protobuf", "protobuf"]` | +| included_packages | A list of packages to include in the generated javadoc. Wildcards are supported at the end of the package name. For example, `com.example.*` will include all the subpackages of `com.example`, while `com.example` will include only the files directly in `com.example`. | List of strings | optional | `[]` | | javadocopts | javadoc options. Note sources and classpath are derived from the deps. Any additional options can be passed here. If nothing is passed, a default list of options is used: ["-notimestamp", "-use", "-quiet", "-Xdoclint:-missing", "-encoding", "UTF8"] | List of strings | optional | `["-notimestamp", "-use", "-quiet", "-Xdoclint:-missing", "-encoding", "UTF8"]` | diff --git a/private/rules/java_export.bzl b/private/rules/java_export.bzl index b49c5dd80..b5d0ef149 100644 --- a/private/rules/java_export.bzl +++ b/private/rules/java_export.bzl @@ -76,6 +76,13 @@ def java_export( (if not using `tags = ["no-javadoc"]`) doc_url: The URL at which the generated `javadoc` will be hosted (if not using `tags = ["no-javadoc"]`). + doc_resources: Resources to be included in the javadoc jar. + doc_excluded_packages: A list of packages to exclude from the generated javadoc. Wildcards are supported at the + end of the package name. For example, `com.example.*` will exclude all the subpackages of `com.example`, while + `com.example` will exclude only the files directly in `com.example` + doc_included_packages: A list of packages to include in the generated javadoc. Wildcards are supported at the + end of the package name. For example, `com.example.*` will include all the subpackages of `com.example`, while + `com.example` will include only the files directly in `com.example` visibility: The visibility of the target kwargs: These are passed to [`java_library`](https://bazel.build/reference/be/java#java_library), and so may contain any valid parameter for that rule.