Reorganize and clarify API doc generator (#132353)
## Description This cleans up a lot of issues with the API doc generation. Here are the main changes: - Rename `dartdoc.dart` to `create_api_docs.dart` - Move the bulk of the operations out of `dev/bots/docs.sh` into `create_api_docs.dart`. - Delete `dashing_postprocess.dart` and `java_and_objc.dart` and incorporate those operations into `create_api_docs.dart`. - Refactor the doc generation into more understandable classes - Bump the snippets tool version to 0.4.0 (the latest one) - Centralize the information gathering about the Flutter repo into the new `FlutterInformation` class. - Clean up the directory handling, and convert to using the `file` package for all file and directory paths. - Add an `--output` option to docs.sh that specifies the location of the output ZIP file containing the docs. - Defaults to placing the output in `dev/docs/api_docs.zip` (i.e. where the previous code generates the file). - Moved all document generation into a temporary folder that is removed once the documents are generated, to avoid VSCode and other IDEs trying to index the thousands of HTML and JS files in the docs output. - Updated pubspec dependencies. ## Tests - Added tests for doc generation.
This commit is contained in:
@@ -1,97 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
const String kDocRoot = 'dev/docs/doc';
|
||||
|
||||
/// This script downloads an archive of Javadoc and objc doc for the engine from
|
||||
/// the artifact store and extracts them to the location used for Dartdoc.
|
||||
Future<void> main(List<String> args) async {
|
||||
final String engineVersion = File('bin/internal/engine.version').readAsStringSync().trim();
|
||||
String engineRealm = File('bin/internal/engine.realm').readAsStringSync().trim();
|
||||
if (engineRealm.isNotEmpty) {
|
||||
engineRealm = '$engineRealm/';
|
||||
}
|
||||
|
||||
final String javadocUrl = 'https://storage.googleapis.com/${engineRealm}flutter_infra_release/flutter/$engineVersion/android-javadoc.zip';
|
||||
generateDocs(javadocUrl, 'javadoc', 'io/flutter/view/FlutterView.html');
|
||||
|
||||
final String objcdocUrl = 'https://storage.googleapis.com/${engineRealm}flutter_infra_release/flutter/$engineVersion/ios-objcdoc.zip';
|
||||
generateDocs(objcdocUrl, 'objcdoc', 'Classes/FlutterViewController.html');
|
||||
}
|
||||
|
||||
/// Fetches the zip archive at the specified url.
|
||||
///
|
||||
/// Returns null if the archive fails to download after [maxTries] attempts.
|
||||
Future<Archive?> fetchArchive(String url, int maxTries) async {
|
||||
List<int>? responseBytes;
|
||||
for (int i = 0; i < maxTries; i++) {
|
||||
final http.Response response = await http.get(Uri.parse(url));
|
||||
if (response.statusCode == 200) {
|
||||
responseBytes = response.bodyBytes;
|
||||
break;
|
||||
}
|
||||
stderr.writeln('Failed attempt ${i+1} to fetch $url.');
|
||||
|
||||
// On failure print a short snipped from the body in case it's helpful.
|
||||
final int bodyLength = min(1024, response.body.length);
|
||||
stderr.writeln('Response status code ${response.statusCode}. Body: ${response.body.substring(0, bodyLength)}');
|
||||
sleep(const Duration(seconds: 1));
|
||||
}
|
||||
return responseBytes == null ? null : ZipDecoder().decodeBytes(responseBytes);
|
||||
}
|
||||
|
||||
Future<void> generateDocs(String url, String docName, String checkFile) async {
|
||||
const int maxTries = 5;
|
||||
final Archive? archive = await fetchArchive(url, maxTries);
|
||||
if (archive == null) {
|
||||
stderr.writeln('Failed to fetch zip archive from: $url after $maxTries attempts. Giving up.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
final Directory output = Directory('$kDocRoot/$docName');
|
||||
print('Extracting $docName to ${output.path}');
|
||||
output.createSync(recursive: true);
|
||||
|
||||
for (final ArchiveFile af in archive) {
|
||||
if (!af.name.endsWith('/')) {
|
||||
final File file = File('${output.path}/${af.name}');
|
||||
file.createSync(recursive: true);
|
||||
file.writeAsBytesSync(af.content as List<int>);
|
||||
}
|
||||
}
|
||||
|
||||
/// If object then copy files to old location if the archive is using the new location.
|
||||
final bool exists = Directory('$kDocRoot/$docName/objectc_docs').existsSync();
|
||||
if (exists) {
|
||||
copyFolder(Directory('$kDocRoot/$docName/objectc_docs'), Directory('$kDocRoot/$docName/'));
|
||||
}
|
||||
|
||||
final File testFile = File('${output.path}/$checkFile');
|
||||
if (!testFile.existsSync()) {
|
||||
print('Expected file ${testFile.path} not found');
|
||||
exit(1);
|
||||
}
|
||||
print('$docName ready to go!');
|
||||
}
|
||||
|
||||
/// Copies the files in a directory recursively to a new location.
|
||||
void copyFolder(Directory source, Directory destination) {
|
||||
source.listSync()
|
||||
.forEach((FileSystemEntity entity) {
|
||||
if (entity is Directory) {
|
||||
final Directory newDirectory = Directory(path.join(destination.absolute.path, path.basename(entity.path)));
|
||||
newDirectory.createSync();
|
||||
copyFolder(entity.absolute, newDirectory);
|
||||
} else if (entity is File) {
|
||||
entity.copySync(path.join(destination.path, path.basename(entity.path)));
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user