xref: /aosp_15_r20/external/cronet/components/cronet/tools/generate_javadoc.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python
2#
3# Copyright 2015 The Chromium Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import argparse
8import itertools
9import os
10import shutil
11import sys
12import tempfile
13
14REPOSITORY_ROOT = os.path.abspath(
15    os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
16
17sys.path.insert(0, os.path.join(REPOSITORY_ROOT, 'build/android/gyp'))
18sys.path.insert(0, os.path.join(REPOSITORY_ROOT, 'net/tools/net_docs'))
19# pylint: disable=wrong-import-position
20from util import build_utils
21import action_helpers  # build_utils adds //build to sys.path.
22import net_docs
23from markdown.postprocessors import Postprocessor
24from markdown.extensions import Extension
25# pylint: enable=wrong-import-position
26
27DOCLAVA_DIR = os.path.join(REPOSITORY_ROOT, 'buildtools', 'android', 'doclava')
28SDK_DIR = os.path.join(REPOSITORY_ROOT, 'third_party', 'android_sdk', 'public')
29# TODO(b/260694901) Remove this usage of Java 11 as soon as Doclava supports it.
30# Doclava support for JDK17 was actively being worked on as of Jan 2023.
31JAVA_11_HOME = os.path.join(REPOSITORY_ROOT, 'third_party', 'jdk11', 'current')
32JAVADOC_PATH = os.path.join(JAVA_11_HOME, 'bin', 'javadoc')
33JAR_PATH = os.path.join(JAVA_11_HOME, 'bin', 'jar')
34
35JAVADOC_WARNING = """\
36javadoc: warning - The old Doclet and Taglet APIs in the packages
37com.sun.javadoc, com.sun.tools.doclets and their implementations
38are planned to be removed in a future JDK release. These
39components have been superseded by the new APIs in jdk.javadoc.doclet.
40Users are strongly recommended to migrate to the new APIs.
41"""
42
43class CronetPostprocessor(Postprocessor):
44  def run(self, text):
45    return text.replace('@Override', '@Override')
46
47
48class CronetExtension(Extension):
49  def extendMarkdown(self, md, md_globals):
50    md.postprocessors.add('CronetPostprocessor', CronetPostprocessor(md),
51                          '_end')
52
53
54def GenerateJavadoc(args, src_dir, output_dir):
55  working_dir = os.path.join(args.input_dir, 'android', 'api')
56  overview_file = os.path.abspath(args.overview_file)
57
58  android_sdk_jar = args.android_sdk_jar
59  if not android_sdk_jar:
60    android_sdk_jar = os.path.join(SDK_DIR, 'platforms', 'android-27',
61                                   'android.jar')
62
63  build_utils.DeleteDirectory(output_dir)
64  build_utils.MakeDirectory(output_dir)
65  classpath = ([android_sdk_jar] + args.support_annotations_jars +
66               args.classpath_jars)
67  javadoc_cmd = [
68      os.path.abspath(JAVADOC_PATH),
69      '-d',
70      output_dir,
71      '-quiet',
72      '-overview',
73      overview_file,
74      '-doclet',
75      'com.google.doclava.Doclava',
76      '-docletpath',
77      '%s:%s' % (os.path.join(DOCLAVA_DIR, 'jsilver.jar'),
78                 os.path.join(DOCLAVA_DIR, 'doclava.jar')),
79      '-title',
80      'Cronet API',
81      '-federate',
82      'Android',
83      'https://developer.android.com/',
84      '-federationapi',
85      'Android',
86      os.path.join(DOCLAVA_DIR, 'current.txt'),
87      '-classpath',
88      ':'.join(os.path.abspath(p) for p in classpath),
89  ]
90  for subdir, _, files in os.walk(src_dir):
91    for filename in files:
92      if filename.endswith(".java"):
93        javadoc_cmd += [os.path.join(subdir, filename)]
94  try:
95
96    def stderr_filter(stderr):
97      return stderr.replace(JAVADOC_WARNING, '')
98
99    build_utils.CheckOutput(javadoc_cmd,
100                            cwd=working_dir,
101                            stderr_filter=stderr_filter)
102  except build_utils.CalledProcessError:
103    build_utils.DeleteDirectory(output_dir)
104    raise
105
106  # Create an index.html file at the root as this is the accepted format.
107  # Do this by copying reference/index.html and adjusting the path.
108  with open(os.path.join(output_dir, 'reference', 'index.html'), 'r') as \
109      old_index, open(os.path.join(output_dir, 'index.html'), 'w') as new_index:
110    for line in old_index:
111      new_index.write(
112          line.replace('classes.html', os.path.join('reference',
113                                                    'classes.html')))
114
115
116def main(argv):
117  parser = argparse.ArgumentParser()
118  action_helpers.add_depfile_arg(parser)
119  parser.add_argument('--output-dir', help='Directory to put javadoc')
120  parser.add_argument('--input-dir', help='Root of cronet source')
121  parser.add_argument('--input-src-jar', help='Cronet api source jar')
122  parser.add_argument('--overview-file', help='Path of the overview page')
123  parser.add_argument('--readme-file', help='Path of the README.md')
124  parser.add_argument('--zip-file', help='Path to ZIP archive of javadocs.')
125  parser.add_argument('--android-sdk-jar', help='Path to android.jar')
126  parser.add_argument('--support-annotations-jars',
127                      help='Path to support-annotations-$VERSION.jar',
128                      action='append',
129                      nargs='*')
130  parser.add_argument('--classpath-jars',
131                      help='Paths to jars needed by support-annotations-jar.',
132                      action='append',
133                      nargs='*')
134  expanded_argv = build_utils.ExpandFileArgs(argv)
135  args, _ = parser.parse_known_args(expanded_argv)
136
137  args.classpath_jars = action_helpers.parse_gn_list(args.classpath_jars)
138
139  args.support_annotations_jars = list(
140      itertools.chain(*args.support_annotations_jars))
141  # A temporary directory to put the output of cronet api source jar files.
142  unzipped_jar_path = tempfile.mkdtemp(dir=args.output_dir)
143  if os.path.exists(args.input_src_jar):
144    jar_cmd = [
145        os.path.relpath(JAR_PATH, unzipped_jar_path), 'xf',
146        os.path.abspath(args.input_src_jar)
147    ]
148    build_utils.CheckOutput(jar_cmd, cwd=unzipped_jar_path)
149  else:
150    raise Exception('Jar file does not exist: %s' % args.input_src_jar)
151
152  net_docs.ProcessDocs([args.readme_file],
153                       args.input_dir,
154                       args.output_dir,
155                       extensions=[CronetExtension()])
156
157  output_dir = os.path.abspath(os.path.join(args.output_dir, 'javadoc'))
158  GenerateJavadoc(args, os.path.abspath(unzipped_jar_path), output_dir)
159
160  if args.zip_file:
161    assert args.zip_file.endswith('.zip')
162    shutil.make_archive(args.zip_file[:-4], 'zip', output_dir)
163  if args.depfile:
164    assert args.zip_file
165    deps = []
166    for root, _, filenames in os.walk(args.input_dir):
167      # Ignore .pyc files here, it might be re-generated during build.
168      deps.extend(
169          os.path.join(root, f) for f in filenames if not f.endswith('.pyc'))
170    if args.support_annotations_jars:
171      deps.extend(args.support_annotations_jars)
172    if args.classpath_jars:
173      deps.extend(args.classpath_jars)
174    action_helpers.write_depfile(args.depfile, args.zip_file, deps)
175  # Clean up temporary output directory.
176  build_utils.DeleteDirectory(unzipped_jar_path)
177
178
179if __name__ == '__main__':
180  sys.exit(main(sys.argv[1:]))
181