blob: 13d2cfecb178f0224a3ed2236d7462711bc03697 [file] [edit]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. 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 'package:markdown/markdown.dart';
import 'package:path/path.dart';
import 'package:pub_semver/pub_semver.dart';
import 'common/generate_common.dart';
import 'dart/generate_dart_client.dart';
import 'dart/generate_dart_common.dart';
import 'dart/generate_dart_interface.dart';
final bool _stampPubspecVersion = false;
/// Parse the 'service.md' into a model and generate Dart libraries.
Future<void> main(List<String> args) async {
final codeGeneratorDir = dirname(Platform.script.toFilePath());
// Parse service.md into a model.
final file = File(
normalize(join(codeGeneratorDir, '../../../runtime/vm/service/service.md')),
);
final document = Document();
final buf = StringBuffer(file.readAsStringSync());
final nodes = document.parseLines(buf.toString().split(RegExp('\r?\n')));
print('Parsed ${file.path}.');
print('Service protocol version ${ApiParseUtil.parseVersionString(nodes)}.');
// Generate code from the model.
print('');
await _generateDartClient(codeGeneratorDir, nodes);
await _generateDartInterface(codeGeneratorDir, nodes);
}
Future<void> _generateDartClient(
String codeGeneratorDir,
List<Node> nodes,
) async {
final outputFilePath = await _generateDartCommon(
api: VmServiceApi(),
nodes: nodes,
codeGeneratorDir: codeGeneratorDir,
packageName: 'vm_service',
interfaceName: 'VmService',
);
print('Wrote Dart client to $outputFilePath.');
}
Future<void> _generateDartInterface(
String codeGeneratorDir,
List<Node> nodes,
) async {
final outputFilePath = await _generateDartCommon(
api: VmServiceInterfaceApi(),
nodes: nodes,
codeGeneratorDir: codeGeneratorDir,
packageName: 'vm_service_interface',
interfaceName: 'VmServiceInterface',
);
print('Wrote Dart interface to $outputFilePath.');
}
Future<String> _generateDartCommon({
required Api api,
required List<Node> nodes,
required String codeGeneratorDir,
required String packageName,
required String interfaceName,
}) async {
final outDirPath = normalize(
join(codeGeneratorDir, '../..', packageName, 'lib/src'),
);
final outDir = Directory(outDirPath);
if (!outDir.existsSync()) {
outDir.createSync(recursive: true);
}
final outputFile = File(join(outDirPath, '$packageName.dart'));
final generator = DartGenerator(interfaceName: interfaceName);
// Generate the code.
api.parse(nodes);
api.generate(generator);
outputFile.writeAsStringSync(generator.toString());
// Clean up the code.
await _runDartFormat(outDirPath);
if (_stampPubspecVersion) {
// Update the pubspec file.
Version version = ApiParseUtil.parseVersionSemVer(nodes);
_stampPubspec(version);
// Validate that the changelog contains an entry for the current version.
_checkUpdateChangelog(version);
}
return outputFile.path;
}
Future<void> _runDartFormat(String outDirPath) async {
ProcessResult result = Process.runSync(Platform.resolvedExecutable, [
'format',
outDirPath,
]);
if (result.exitCode != 0) {
print('dart format: ${result.stdout}\n${result.stderr}');
throw result.exitCode;
}
}
// Push the major and minor versions into the pubspec.
void _stampPubspec(Version version) {
final String pattern = 'version: ';
File file = File('pubspec.yaml');
String text = file.readAsStringSync();
bool found = false;
text = text
.split('\n')
.map((line) {
if (line.startsWith(pattern)) {
found = true;
Version v = Version.parse(line.substring(pattern.length));
String? pre = v.preRelease.isEmpty ? null : v.preRelease.join('-');
String? build = v.build.isEmpty ? null : v.build.join('+');
v = Version(
version.major,
version.minor,
v.patch,
pre: pre,
build: build,
);
return '$pattern${v.toString()}';
} else {
return line;
}
})
.join('\n');
if (!found) throw '`$pattern` not found';
file.writeAsStringSync(text);
}
void _checkUpdateChangelog(Version version) {
// Look for `## major.minor`.
String check = '## ${version.major}.${version.minor}';
File file = File('CHANGELOG.md');
String text = file.readAsStringSync();
bool containsReleaseNotes = text
.split('\n')
.any((line) => line.startsWith(check));
if (!containsReleaseNotes) {
throw '`$check` not found in the CHANGELOG.md file';
}
}