| // Copyright (c) 2025, 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:args/args.dart'; |
| import 'package:native_test_helpers/native_test_helpers.dart'; |
| |
| void main(List<String> args) { |
| final stopwatch = Stopwatch()..start(); |
| final parser = ArgParser() |
| ..addFlag( |
| 'set-exit-if-changed', |
| negatable: false, |
| help: 'Return a non-zero exit code if any files were changed.', |
| ); |
| final argResults = parser.parse(args); |
| final setExitIfChanged = argResults['set-exit-if-changed'] as bool; |
| |
| final counts = Counts(); |
| |
| updateManifests(counts); |
| |
| stopwatch.stop(); |
| final duration = stopwatch.elapsedMilliseconds / 1000.0; |
| print( |
| 'Generated ${counts.generated} files (${counts.changed} changed) in ' |
| '${duration.toStringAsFixed(2)} seconds.', |
| ); |
| if (setExitIfChanged && counts.changed > 0) { |
| exit(1); |
| } |
| } |
| |
| class Counts { |
| int generated = 0; |
| int changed = 0; |
| } |
| |
| void updateManifests(Counts counts) async { |
| final packageUri = findPackageRoot('hooks_runner'); |
| final testDataUri = packageUri.resolve('test_data/'); |
| final testDataDirectory = Directory.fromUri(testDataUri); |
| updateManifest(testDataDirectory, counts, allowPartialProjects: false); |
| final all = testDataDirectory.listSync(recursive: true); |
| all.whereType<Directory>().forEach( |
| (e) => updateManifest(e, counts, allowPartialProjects: true), |
| ); |
| } |
| |
| const denyList = [ |
| '.dart_tool', |
| 'pubspec.lock', |
| 'manifest', |
| 'README.md', |
| '.gitignore', |
| ]; |
| |
| /// These just modify other test projects. |
| /// |
| /// Don't add pubspecs to manifests, they contain a different name due to all |
| /// being part of a big workspace. They have a pubspec to enable analysis of |
| /// the Dart code in them. |
| const partialProjects = [ |
| 'native_add_add_source', |
| 'native_add_add_symbol', |
| 'native_add_break_build', |
| 'native_add_fix_build', |
| 'simple_link_change_asset', |
| ]; |
| |
| void updateManifest( |
| Directory directory, |
| Counts counts, { |
| required bool allowPartialProjects, |
| }) { |
| final manifestFile = File.fromUri(directory.uri.resolve('manifest.yaml')); |
| if (!manifestFile.existsSync()) { |
| return; |
| } |
| final all = directory.listSync(recursive: true); |
| final dirPath = directory.uri.toFilePath(windows: false); |
| final files = |
| all |
| .whereType<File>() |
| .where((f) { |
| for (final denyString in [ |
| ...denyList, |
| if (!allowPartialProjects) ...partialProjects, |
| for (final partialProject in partialProjects) ...[ |
| '$partialProject/pubspec.yaml', |
| ], |
| ]) { |
| if (f.uri.toFilePath(windows: false).contains(denyString)) { |
| return false; |
| } |
| } |
| |
| return true; |
| }) |
| .map( |
| (e) => e.uri.toFilePath(windows: false).replaceFirst(dirPath, ''), |
| ) |
| .toList() |
| ..sort(); |
| |
| var oldContent = ''; |
| if (manifestFile.existsSync()) { |
| oldContent = manifestFile.readAsStringSync(); |
| } |
| final newContent = header + files.map((e) => '- $e\n').join(); |
| final newContentNormalized = newContent.replaceAll('\r\n', '\n'); |
| final oldContentNormalized = oldContent.replaceAll('\r\n', '\n'); |
| if (newContentNormalized != oldContentNormalized) { |
| manifestFile.writeAsStringSync(newContent); |
| print('Generated ${manifestFile.uri} (content changed)'); |
| counts.changed++; |
| } |
| counts.generated++; |
| } |
| |
| const header = ''' |
| # The list of files to copy to a temporary folder to ensure running tests from |
| # a completely clean setup. |
| # Automatically generated by manifest_generator.dart. |
| '''; |