blob: 3940f2793f818b8926dfc837d5b42da36ee5c815 [file] [edit]
// 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'));