| // Copyright (c) 2026, 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'; | |
| const _licenseHeader = ''' | |
| // Copyright (c) 2026, 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. | |
| '''; | |
| const _licenseTestString = 'Copyright (c) '; | |
| void main(List<String> arguments) async { | |
| final parser = ArgParser() | |
| ..addFlag( | |
| 'set-exit-if-changed', | |
| negatable: false, | |
| help: 'Return a non-zero exit code if any files were changed.', | |
| ) | |
| ..addFlag( | |
| 'help', | |
| abbr: 'h', | |
| negatable: false, | |
| help: 'Prints this help message.', | |
| ); | |
| final argResults = parser.parse(arguments); | |
| if (argResults['help'] as bool) { | |
| print('Usage: dart tool/check_licenses.dart [directories]'); | |
| print(parser.usage); | |
| return; | |
| } | |
| final directories = argResults.rest.isEmpty ? ['.'] : argResults.rest; | |
| final setExitIfChanged = argResults['set-exit-if-changed'] as bool; | |
| var generatedCount = 0; | |
| var changedCount = 0; | |
| final stopwatch = Stopwatch()..start(); | |
| for (final dirPath in directories) { | |
| final directory = Directory(dirPath); | |
| if (!directory.existsSync()) { | |
| print('Directory not found: $dirPath'); | |
| continue; | |
| } | |
| await for (final file in directory.list(recursive: true)) { | |
| if (file is! File || !file.path.endsWith('.dart')) { | |
| continue; | |
| } | |
| if (_isIgnored(file.path)) { | |
| continue; | |
| } | |
| generatedCount++; | |
| final contents = await file.readAsString(); | |
| if (_fileIsGenerated(contents, file.path)) { | |
| continue; | |
| } | |
| if (!contents.contains(_licenseTestString)) { | |
| print('Adding license header to ${file.path}'); | |
| final newContents = '${_licenseHeader.trimLeft()}\n$contents'; | |
| await file.writeAsString(newContents); | |
| changedCount++; | |
| } | |
| } | |
| } | |
| final seconds = stopwatch.elapsedMilliseconds / 1000.0; | |
| print( | |
| 'Checked $generatedCount files ($changedCount changed) in ' | |
| '${seconds.toStringAsFixed(2)} seconds.', | |
| ); | |
| if (setExitIfChanged && changedCount > 0) { | |
| exit(1); | |
| } | |
| } | |
| bool _isIgnored(String filePath) { | |
| final segments = filePath.split(RegExp(r'[/\\]')); | |
| for (var i = 0; i < segments.length; i++) { | |
| final segment = segments[i]; | |
| if (segment == '.dart_tool' || segment == '.git' || segment == '.jj') { | |
| return true; | |
| } | |
| if (segment == 'build') { | |
| // Only ignore 'build' if it's a sibling of 'pubspec.yaml'. | |
| final parentPath = segments.take(i).join(Platform.pathSeparator); | |
| final pubspec = File( | |
| parentPath.isEmpty | |
| ? 'pubspec.yaml' | |
| : '$parentPath${Platform.pathSeparator}pubspec.yaml', | |
| ); | |
| if (pubspec.existsSync()) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| bool _fileIsGenerated(String fileContents, String filePath) => | |
| filePath.endsWith('.g.dart') || | |
| fileContents | |
| .split('\n') | |
| .map((line) => line.trim()) | |
| .takeWhile((line) => line.startsWith('//') || line.isEmpty) | |
| .any((line) => line.toLowerCase().contains('generate')); |