1// Copyright (C) 2017 The Android Open Source Project
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.
14apply from: "${buildscript.sourceFile.parentFile}/constants.gradle"
15apply from: "${buildscript.sourceFile.parentFile}/javadoc_util.gradle"
16
17class CombinedJavadocPlugin implements Plugin<Project> {
18
19  static final String JAVADOC_TASK_NAME = "generateCombinedJavadoc"
20  static final String DACKKA_TASK_NAME = "generateCombinedDackka"
21
22  // Dackka snapshots are listed at https://androidx.dev/dackka/builds.
23  static final String DACKKA_JAR_URL =
24      "https://androidx.dev/dackka/builds/8003564/artifacts/dackka-0.0.14.jar"
25
26  @Override
27  void apply(Project project) {
28    project.gradle.projectsEvaluated {
29      Set<Project> libraryModules = getLibraryModules(project)
30      if (!libraryModules.isEmpty()) {
31        def guavaReferenceUrl = "https://guava.dev/releases/$project.ext.guavaVersion/api/docs"
32
33        project.task(JAVADOC_TASK_NAME, type: Javadoc) {
34          description = "Generates combined Javadoc."
35          title = "ExoPlayer library"
36          source = libraryModules.generateJavadoc.source
37          classpath = project.files([])
38          destinationDir = project.file("$project.buildDir/docs/javadoc")
39          options {
40            links "https://developer.android.com/reference", guavaReferenceUrl
41            encoding = "UTF-8"
42          }
43          options.addBooleanOption "-no-module-directories", true
44          exclude "**/BuildConfig.java"
45          exclude "**/R.java"
46          doFirst {
47            libraryModules.each { libraryModule ->
48              libraryModule.android.libraryVariants.all { variant ->
49                def name = variant.buildType.name
50                if (name == "release") {
51                  classpath +=
52                      libraryModule.project.files(
53                          variant.javaCompileProvider.get().classpath.files,
54                          libraryModule.project.android.getBootClasspath())
55                }
56              }
57            }
58          }
59          doLast {
60            libraryModules.each { libraryModule ->
61              project.copy {
62                from "${libraryModule.projectDir}/src/main/javadoc"
63                into "${project.buildDir}/docs/javadoc"
64              }
65            }
66            project.fixJavadoc()
67          }
68        }
69
70        def dackkaOutputDir = project.file("$project.buildDir/docs/dackka")
71        project.task(DACKKA_TASK_NAME, type: JavaExec) {
72          doFirst {
73            // Recreate the output directory to remove any leftover files from a previous run.
74            project.delete dackkaOutputDir
75            project.mkdir dackkaOutputDir
76
77            // Download the Dackka JAR.
78            new URL(DACKKA_JAR_URL).withInputStream {
79              i -> classpath.getSingleFile().withOutputStream { it << i }
80            }
81
82            // Build lists of source files and dependencies.
83            def sources = []
84            def dependencies = []
85            libraryModules.each { libraryModule ->
86              libraryModule.android.libraryVariants.all { variant ->
87                def name = variant.buildType.name
88                if (name == "release") {
89                  def classpathFiles =
90                      project.files(variant.javaCompileProvider.get().classpath.files)
91                  variant.sourceSets.inject(sources) {
92                    acc, val -> acc << val.javaDirectories
93                  }
94                  dependencies << classpathFiles.filter { f -> !(f.path.contains("/buildout/")) }
95                  dependencies << libraryModule.project.android.getBootClasspath()
96                }
97              }
98            }
99
100            // Set command line arguments to Dackka.
101            def guavaPackageListFile = getGuavaPackageListFile(getTemporaryDir())
102            def globalLinksString = "$guavaReferenceUrl^$guavaPackageListFile^^"
103            def sourcesString = project.files(sources.flatten())
104                .filter({ f -> project.file(f).exists() }).join(";")
105            def dependenciesString = project.files(dependencies).asPath.replace(':', ';')
106            args("-moduleName", "",
107                "-outputDir", "$dackkaOutputDir",
108                "-globalLinks", "$globalLinksString",
109                "-loggingLevel", "WARN",
110                "-sourceSet", "-src $sourcesString -classpath $dependenciesString",
111                "-offlineMode")
112            environment("DEVSITE_TENANT", "androidx/media3")
113          }
114          description = "Generates combined javadoc for developer.android.com."
115          classpath = project.files(new File(getTemporaryDir(), "dackka.jar"))
116          doLast {
117            libraryModules.each { libraryModule ->
118              project.copy {
119                from "${libraryModule.projectDir}/src/main/javadoc"
120                into "${dackkaOutputDir}/reference/"
121              }
122              project.copy {
123                from "${libraryModule.projectDir}/src/main/javadoc"
124                into "${dackkaOutputDir}/reference/kotlin/"
125              }
126            }
127          }
128        }
129      }
130    }
131  }
132
133  // Returns Android library modules that declare a generateJavadoc task.
134  private static Set<Project> getLibraryModules(Project project) {
135    project.subprojects.findAll {
136      it.plugins.findPlugin("com.android.library") &&
137      it.tasks.findByName("generateJavadoc")
138    }
139  }
140
141  // Returns a file containing the list of packages that should be linked to Guava documentation.
142  private static File getGuavaPackageListFile(File directory) {
143    def packageListFile = new File(directory, "guava")
144    packageListFile.text = ["com.google.common.base", "com.google.common.collect",
145                            "com.google.common.io", "com.google.common.math",
146                            "com.google.common.net", "com.google.common.primitives",
147                            "com.google.common.truth", "com.google.common.util.concurrent"]
148        .join('\n')
149    return packageListFile
150  }
151
152}
153
154apply plugin: CombinedJavadocPlugin
155