mirror of
https://github.com/flutter/packages.git
synced 2025-08-16 12:46:48 +08:00
Standardize copyright year (#3737)
Standardizes all first-party copyrights on a single year, as is done in flutter/flutter and flutter/engine. All code now uses 2013, which is the earliest year that was in any existing copyright notice. The script checks now enforce the exact format of first-party licenses and copyrights. Fixes flutter/flutter#78448
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 The Flutter Authors. All rights reserved.
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@ -41,34 +41,30 @@ const Set<String> _ignoredFullBasenameList = <String>{
|
||||
'resource.h', // Generated by VS.
|
||||
};
|
||||
|
||||
// Copyright and license regexes.
|
||||
// Copyright and license regexes for third-party code.
|
||||
//
|
||||
// These are intentionally very simple, since almost all source in this
|
||||
// repository should be using the same license text, comment style, etc., so
|
||||
// they shouldn't need to be very flexible. Complexity can be added as-needed
|
||||
// on a case-by-case basis.
|
||||
final RegExp _copyrightRegex =
|
||||
RegExp(r'^(?://|#|<!--) Copyright \d+,? ([^.]+)', multiLine: true);
|
||||
// Non-Flutter code. When adding license regexes here, include the copyright
|
||||
// info to ensure that any new additions are flagged for added scrutiny in
|
||||
// review.
|
||||
// -----
|
||||
// These are intentionally very simple, since there is very little third-party
|
||||
// code in this repository. Complexity can be added as-needed on a case-by-case
|
||||
// basis.
|
||||
//
|
||||
// When adding license regexes here, include the copyright info to ensure that
|
||||
// any new additions are flagged for added scrutiny in review.
|
||||
final List<RegExp> _thirdPartyLicenseBlockRegexes = <RegExp>[
|
||||
// Third-party code used in url_launcher_web.
|
||||
final RegExp _workivaLicenseRegex = RegExp(
|
||||
r'^// Copyright 2017 Workiva Inc..*'
|
||||
'^// Licensed under the Apache License, Version 2.0',
|
||||
multiLine: true,
|
||||
dotAll: true);
|
||||
|
||||
const String _firstPartyAuthors = 'The Flutter Authors';
|
||||
RegExp(
|
||||
r'^// Copyright 2017 Workiva Inc..*'
|
||||
'^// Licensed under the Apache License, Version 2.0',
|
||||
multiLine: true,
|
||||
dotAll: true),
|
||||
];
|
||||
|
||||
// The exact format of the BSD license that our license files should contain.
|
||||
// Slight variants are not accepted because they may prevent consolidation in
|
||||
// tools that assemble all licenses used in distributed applications.
|
||||
//
|
||||
// TODO(stuartmorgan): Add the copyright string here once that's completely
|
||||
// standardized.
|
||||
final String _fullBsdLicenseText = '''
|
||||
final String _fullBsdLicenseText =
|
||||
'''Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
@ -132,91 +128,80 @@ class LicenseCheckCommand extends PluginCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the expected license block (without copyright) for first-party
|
||||
// code.
|
||||
String _generateLicense(String comment, {String suffix = ''}) {
|
||||
return '${comment}Use of this source code is governed by a BSD-style license that can be\n'
|
||||
// Creates the expected copyright+license block for first-party code.
|
||||
String _generateLicenseBlock(
|
||||
String comment, {
|
||||
String prefix = '',
|
||||
String suffix = '',
|
||||
}) {
|
||||
return '$prefix${comment}Copyright 2013 The Flutter Authors. All rights reserved.\n'
|
||||
'${comment}Use of this source code is governed by a BSD-style license that can be\n'
|
||||
'${comment}found in the LICENSE file.$suffix\n';
|
||||
}
|
||||
|
||||
// Checks all license blocks for [codeFiles], returning false if any of them
|
||||
// fail validation.
|
||||
Future<bool> _checkCodeLicenses(Iterable<File> codeFiles) async {
|
||||
final List<File> filesWithoutDetectedCopyright = <File>[];
|
||||
final List<File> filesWithoutDetectedLicense = <File>[];
|
||||
final List<File> misplacedThirdPartyFiles = <File>[];
|
||||
final List<File> incorrectFirstPartyFiles = <File>[];
|
||||
final List<File> unrecognizedThirdPartyFiles = <File>[];
|
||||
|
||||
// Most code file types in the repository use '//' comments.
|
||||
final String defaultBsdLicenseBlock = _generateLicense('// ');
|
||||
final String defaultFirstParyLicenseBlock = _generateLicenseBlock('// ');
|
||||
// A few file types have a different comment structure.
|
||||
final Map<String, String> bsdLicenseBlockByExtension = <String, String>{
|
||||
'.sh': _generateLicense('# '),
|
||||
'.html': _generateLicense('', suffix: ' -->'),
|
||||
final Map<String, String> firstPartyLicenseBlockByExtension =
|
||||
<String, String>{
|
||||
'.sh': _generateLicenseBlock('# '),
|
||||
'.html': _generateLicenseBlock('', prefix: '<!-- ', suffix: ' -->'),
|
||||
};
|
||||
|
||||
for (final File file in codeFiles) {
|
||||
_print('Checking ${file.path}');
|
||||
final String content = await file.readAsString();
|
||||
|
||||
final RegExpMatch copyright = _copyrightRegex.firstMatch(content);
|
||||
if (copyright == null) {
|
||||
filesWithoutDetectedCopyright.add(file);
|
||||
continue;
|
||||
}
|
||||
final String author = copyright.group(1);
|
||||
if (author != _firstPartyAuthors && !_isThirdParty(file)) {
|
||||
misplacedThirdPartyFiles.add(file);
|
||||
}
|
||||
|
||||
final String bsdLicense =
|
||||
bsdLicenseBlockByExtension[p.extension(file.path)] ??
|
||||
defaultBsdLicenseBlock;
|
||||
if (!content.contains(bsdLicense) &&
|
||||
!_workivaLicenseRegex.hasMatch(content)) {
|
||||
filesWithoutDetectedLicense.add(file);
|
||||
if (_isThirdParty(file)) {
|
||||
if (!_thirdPartyLicenseBlockRegexes
|
||||
.any((regex) => regex.hasMatch(content))) {
|
||||
unrecognizedThirdPartyFiles.add(file);
|
||||
}
|
||||
} else {
|
||||
final String license =
|
||||
firstPartyLicenseBlockByExtension[p.extension(file.path)] ??
|
||||
defaultFirstParyLicenseBlock;
|
||||
if (!content.contains(license)) {
|
||||
incorrectFirstPartyFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
_print('\n');
|
||||
|
||||
// Sort by path for more usable output.
|
||||
final pathCompare = (File a, File b) => a.path.compareTo(b.path);
|
||||
filesWithoutDetectedCopyright.sort(pathCompare);
|
||||
filesWithoutDetectedLicense.sort(pathCompare);
|
||||
misplacedThirdPartyFiles.sort(pathCompare);
|
||||
incorrectFirstPartyFiles.sort(pathCompare);
|
||||
unrecognizedThirdPartyFiles.sort(pathCompare);
|
||||
|
||||
if (filesWithoutDetectedCopyright.isNotEmpty) {
|
||||
_print('No copyright line was found for the following files:');
|
||||
for (final File file in filesWithoutDetectedCopyright) {
|
||||
if (incorrectFirstPartyFiles.isNotEmpty) {
|
||||
_print('The license block for these files is missing or incorrect:');
|
||||
for (final File file in incorrectFirstPartyFiles) {
|
||||
_print(' ${file.path}');
|
||||
}
|
||||
_print('Please check that they have a copyright and license block. '
|
||||
'If they do, the license check may need to be updated to recognize its '
|
||||
'format.\n');
|
||||
_print('If this third-party code, move it to a "third_party/" directory, '
|
||||
'otherwise ensure that you are using the exact copyright and license '
|
||||
'text used by all first-party files in this repository.\n');
|
||||
}
|
||||
|
||||
if (filesWithoutDetectedLicense.isNotEmpty) {
|
||||
_print('No recognized license was found for the following files:');
|
||||
for (final File file in filesWithoutDetectedLicense) {
|
||||
if (unrecognizedThirdPartyFiles.isNotEmpty) {
|
||||
_print(
|
||||
'No recognized license was found for the following third-party files:');
|
||||
for (final File file in unrecognizedThirdPartyFiles) {
|
||||
_print(' ${file.path}');
|
||||
}
|
||||
_print('Please check that they have a license at the top of the file. '
|
||||
'If they do, the license check may need to be updated to recognize '
|
||||
'either the license or the specific format of the license '
|
||||
'block.\n');
|
||||
'If they do, the license check needs to be updated to recognize '
|
||||
'the new third-party license block.\n');
|
||||
}
|
||||
|
||||
if (misplacedThirdPartyFiles.isNotEmpty) {
|
||||
_print('The following files do not have a recognized first-party author '
|
||||
'but are not in a "third_party/" directory:');
|
||||
for (final File file in misplacedThirdPartyFiles) {
|
||||
_print(' ${file.path}');
|
||||
}
|
||||
_print('Please move these files to "third_party/".\n');
|
||||
}
|
||||
|
||||
bool succeeded = filesWithoutDetectedCopyright.isEmpty &&
|
||||
filesWithoutDetectedLicense.isEmpty &&
|
||||
misplacedThirdPartyFiles.isEmpty;
|
||||
bool succeeded =
|
||||
incorrectFirstPartyFiles.isEmpty && unrecognizedThirdPartyFiles.isEmpty;
|
||||
if (succeeded) {
|
||||
_print('All source files passed validation!');
|
||||
}
|
||||
@ -241,7 +226,8 @@ class LicenseCheckCommand extends PluginCommand {
|
||||
for (final File file in incorrectLicenseFiles) {
|
||||
_print(' ${file.path}');
|
||||
}
|
||||
_print('Please ensure that they use the exact format used in this repository".\n');
|
||||
_print(
|
||||
'Please ensure that they use the exact format used in this repository".\n');
|
||||
}
|
||||
|
||||
bool succeeded = incorrectLicenseFiles.isEmpty;
|
||||
|
Reference in New Issue
Block a user