// 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.dart' as dart show Api, api, DartGenerator; import 'java/generate_java.dart' as java show Api, api, JavaGenerator; final bool _stampPubspecVersion = false; /// Parse the 'service.md' into a model and generate both Dart and Java /// libraries. main(List args) async { String appDirPath = dirname(Platform.script.toFilePath()); // Parse service.md into a model. var file = File( normalize(join(appDirPath, '../../../runtime/vm/service/service.md'))); var document = Document(); StringBuffer buf = StringBuffer(file.readAsStringSync()); var nodes = document.parseLines(buf.toString().split('\n')); print('Parsed ${file.path}.'); print('Service protocol version ${ApiParseUtil.parseVersionString(nodes)}.'); // Generate code from the model. print(''); await _generateDart(appDirPath, nodes); await _generateJava(appDirPath, nodes); await _generateAsserts(appDirPath, nodes); } _generateDart(String appDirPath, List nodes) async { var outDirPath = normalize(join(appDirPath, '..', 'lib/src')); var outDir = Directory(outDirPath); if (!outDir.existsSync()) outDir.createSync(recursive: true); var outputFile = File(join(outDirPath, 'vm_service.dart')); var generator = dart.DartGenerator(); dart.api = dart.Api(); dart.api.parse(nodes); dart.api.generate(generator); outputFile.writeAsStringSync(generator.toString()); ProcessResult result = Process.runSync('dartfmt', ['-w', outDirPath]); if (result.exitCode != 0) { print('dartfmt: ${result.stdout}\n${result.stderr}'); throw result.exitCode; } 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); } print('Wrote Dart to ${outputFile.path}.'); } _generateJava(String appDirPath, List nodes) async { var srcDirPath = normalize(join(appDirPath, '..', 'java', 'src')); var generator = java.JavaGenerator(srcDirPath); java.api = java.Api(); java.api.parse(nodes); java.api.generate(generator); // We generate files into the java/src/ folder; ensure the generated files // aren't committed to git (but manually maintained files in the same // directory are). List generatedPaths = generator.allWrittenFiles .map((path) => relative(path, from: 'java')) .toList(); generatedPaths.sort(); File gitignoreFile = File(join(appDirPath, '..', 'java', '.gitignore')); gitignoreFile.writeAsStringSync(''' # This is a generated file. ${generatedPaths.join('\n')} '''); // Generate a version file. Version version = ApiParseUtil.parseVersionSemVer(nodes); File file = File(join('java', 'version.properties')); file.writeAsStringSync('version=${version.major}.${version.minor}\n'); print('Wrote Java to $srcDirPath.'); } _generateAsserts(String appDirPath, List nodes) async { var outDirPath = normalize(join(appDirPath, '..', 'example')); var outDir = Directory(outDirPath); if (!outDir.existsSync()) outDir.createSync(recursive: true); var outputFile = File(join(outDirPath, 'vm_service_assert.dart')); var generator = dart.DartGenerator(); dart.api = dart.Api(); dart.api.parse(nodes); dart.api.generateAsserts(generator); outputFile.writeAsStringSync(generator.toString()); ProcessResult result = Process.runSync('dartfmt', ['-w', outDirPath]); if (result.exitCode != 0) { print('dartfmt: ${result.stdout}\n${result.stderr}'); throw result.exitCode; } 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); } print('Wrote Dart to ${outputFile.path}.'); } // 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'; } }