import 'dart:async'; import 'dart:io'; import 'package:analyzer_utilities/tools.dart'; import 'package:package_config/package_config.dart'; import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; Future _formatText(String text, {required String pkgPath}) async { var packageConfig = await findPackageConfig(Directory(pkgPath)); if (packageConfig == null) { throw StateError( 'Could not find the shared Dart SDK package_config.json file, for ' '"$pkgPath"', ); } var package = packageConfig.packageOf( Uri.file(join(pkgPath, 'pubspec.yaml')), ); if (package == null) { throw StateError('Could not find the package for "$pkgPath"'); } var languageVersion = package.languageVersion; if (languageVersion == null) { throw StateError('Could not find a Dart language version for "$pkgPath"'); } var version = Version(languageVersion.major, languageVersion.minor, 0); return DartFormat.formatString(text, languageVersion: version); } extension GeneratedContentExtension on GeneratedContent { /// Check whether the [output] has the correct contents, and return true if it /// does. /// /// [pkgRoot] is the path to the SDK's `pkg` directory. Future check(String pkgRoot) async { switch (this) { case GeneratedDirectory self: var outputDirectory = self.output(pkgRoot); var map = self.directoryContentsComputer(pkgRoot); try { for (var entry in map.entries) { var file = entry.key; var fileContentsComputer = entry.value; var expectedContents = await fileContentsComputer(pkgRoot); var outputFile = File(posix.join(outputDirectory.path, file)); var actualContents = outputFile.readAsStringSync(); // Normalize Windows line endings to Unix line endings so that the // comparison doesn't fail on Windows. actualContents = actualContents.replaceAll('\r\n', '\n'); if (expectedContents != actualContents) { return false; } } var nonHiddenFileCount = 0; outputDirectory .listSync(recursive: false, followLinks: false) .forEach((FileSystemEntity fileSystemEntity) { if (fileSystemEntity is File && !basename(fileSystemEntity.path).startsWith('.')) { nonHiddenFileCount++; } }); if (nonHiddenFileCount != map.length) { // The number of files generated doesn't match the number we expected to // generate. return false; } } catch (e) { // There was a problem reading the file (most likely because it didn't // exist). Treat that the same as if the file doesn't have the expected // contents. return false; } return true; case GeneratedFile self: var outputFile = self.output(pkgRoot); var expectedContents = await self.computeContents(pkgRoot); if (self.isDartFile) { expectedContents = await _formatText( expectedContents, pkgPath: dirname(outputFile.path), ); } try { var actualContents = outputFile.readAsStringSync(); // Normalize Windows line endings to Unix line endings so that the // comparison doesn't fail on Windows. actualContents = actualContents.replaceAll('\r\n', '\n'); expectedContents = expectedContents.replaceAll('\r\n', '\n'); return expectedContents == actualContents; } catch (e) { // There was a problem reading the file (most likely because it didn't // exist). Treat that the same as if the file doesn't have the expected // contents. return false; } } } } extension GeneratedContentIterable on Iterable { /// Check that all of the targets in `this` are up to date. If they are not, /// print out a message instructing the user to regenerate them, and exit with /// a nonzero error code. /// /// [pkgRoot] is the path to the SDK's `pkg` directory. [generatorPath] is /// the path to a .dart script the user may use to regenerate the targets. /// /// To avoid mistakes when run on Windows, [generatorPath] always uses /// POSIX directory separators. Future check(String pkgRoot, String generatorPath) async { var generateNeeded = false; for (var target in this) { var ok = await target.check(pkgRoot); if (!ok) { print( '${normalize(target.output(pkgRoot).absolute.path)}' " doesn't have expected contents.", ); generateNeeded = true; } } if (generateNeeded) { print('Please regenerate using:'); var executable = Platform.executable; var generateScript = normalize(joinAll(posix.split(generatorPath))); print(' $executable $generateScript'); fail('Generated content needs to be regenerated'); } } }