Pre-format licenses script (flutter/engine#57219)

The license script has load-baring formatting: Changing its format makes signature checks fail. To ensure the formatting change goes in smoothly later this week I am taking it off the critical path by pre-formatting it. 

The script itself is fairly stable, so I am not expecting any changes to it between now and when we actually enforce formatting.

Edit: The issue that surfaced with `ci/licenses_golden/licenses_dart` is another reason to take this off the critical path of enforcing formatting.
This commit is contained in:
Michael Goderbauer 2024-12-16 16:19:17 -08:00 committed by GitHub
parent 93d08f2c1a
commit d00bfbb87a
9 changed files with 1954 additions and 1322 deletions

View File

@ -1,2 +1,2 @@
Signature: 08cd2e281007e92182d3d540ff50e0ad
Signature: ddf9867e4034dc9bd94a9e107c31752e

View File

@ -26,9 +26,17 @@ enum FileType {
typedef Reader = List<int> Function();
class BytesOf extends Key { BytesOf(super.value); }
class UTF8Of extends Key { UTF8Of(super.value); }
class Latin1Of extends Key { Latin1Of(super.value); }
class BytesOf extends Key {
BytesOf(super.value);
}
class UTF8Of extends Key {
UTF8Of(super.value);
}
class Latin1Of extends Key {
Latin1Of(super.value);
}
bool matchesSignature(List<int> bytes, List<int> signature) {
if (bytes.length < signature.length) {
@ -66,71 +74,136 @@ const String kMultiLicenseFileHeader = 'Notices for files contained in';
bool isMultiLicenseNotice(Reader reader) {
final List<int> bytes = reader();
return ascii.decode(bytes.take(kMultiLicenseFileHeader.length).toList(), allowInvalid: true) == kMultiLicenseFileHeader;
return ascii.decode(bytes.take(kMultiLicenseFileHeader.length).toList(), allowInvalid: true) ==
kMultiLicenseFileHeader;
}
FileType identifyFile(String name, Reader reader) {
List<int>? bytes;
if ((path.split(name).reversed.take(6).toList().reversed.join('/') == 'third_party/icu/source/extra/uconv/README') || // This specific ICU README isn't in UTF-8.
(path.split(name).reversed.take(6).toList().reversed.join('/') == 'third_party/icu/source/samples/uresb/sr.txt') || // This specific sample contains non-UTF-8 data (unlike other sr.txt files).
(path.split(name).reversed.take(2).toList().reversed.join('/') == 'builds/detect.mk') || // This specific freetype sample contains non-UTF-8 data (unlike other .mk files).
if ((path.split(name).reversed.take(6).toList().reversed.join('/') ==
'third_party/icu/source/extra/uconv/README') || // This specific ICU README isn't in UTF-8.
(path.split(name).reversed.take(6).toList().reversed.join('/') ==
'third_party/icu/source/samples/uresb/sr.txt') || // This specific sample contains non-UTF-8 data (unlike other sr.txt files).
(path.split(name).reversed.take(2).toList().reversed.join('/') ==
'builds/detect.mk') || // This specific freetype sample contains non-UTF-8 data (unlike other .mk files).
(path.split(name).reversed.take(3).toList().reversed.join('/') == 'third_party/cares/cares.rc')) {
return FileType.latin1Text;
}
if (path.split(name).reversed.take(6).toList().reversed.join('/') == 'dart/runtime/tests/vm/dart/bad_snapshot') { // Not any particular format
if (path.split(name).reversed.take(6).toList().reversed.join('/') ==
'dart/runtime/tests/vm/dart/bad_snapshot') {
// Not any particular format
return FileType.binary;
}
if (path.split(name).reversed.take(9).toList().reversed.join('/') == 'fuchsia/sdk/linux/dart/zircon/lib/src/fakes/handle_disposition.dart' || // has bogus but benign "authors" reference, reported to jamesr@
path.split(name).reversed.take(6).toList().reversed.join('/') == 'third_party/angle/src/common/fuchsia_egl/fuchsia_egl.c' || // has bogus but benign "authors" reference, reported to author and legal team
path.split(name).reversed.take(6).toList().reversed.join('/') == 'third_party/angle/src/common/fuchsia_egl/fuchsia_egl.h' || // has bogus but benign "authors" reference, reported to author and legal team
path.split(name).reversed.take(6).toList().reversed.join('/') == 'third_party/angle/src/common/fuchsia_egl/fuchsia_egl_backend.h') { // has bogus but benign "authors" reference, reported to author and legal team
if (path.split(name).reversed.take(9).toList().reversed.join('/') ==
'fuchsia/sdk/linux/dart/zircon/lib/src/fakes/handle_disposition.dart' || // has bogus but benign "authors" reference, reported to jamesr@
path.split(name).reversed.take(6).toList().reversed.join('/') ==
'third_party/angle/src/common/fuchsia_egl/fuchsia_egl.c' || // has bogus but benign "authors" reference, reported to author and legal team
path.split(name).reversed.take(6).toList().reversed.join('/') ==
'third_party/angle/src/common/fuchsia_egl/fuchsia_egl.h' || // has bogus but benign "authors" reference, reported to author and legal team
path.split(name).reversed.take(6).toList().reversed.join('/') ==
'third_party/angle/src/common/fuchsia_egl/fuchsia_egl_backend.h') {
// has bogus but benign "authors" reference, reported to author and legal team
return FileType.binary;
}
if (path.split(name).reversed.take(6).toList().reversed.join('/') == 'flutter/third_party/brotli/c/common/dictionary.bin.br') { // Brotli-compressed Brotli dictionary
if (path.split(name).reversed.take(6).toList().reversed.join('/') ==
'flutter/third_party/brotli/c/common/dictionary.bin.br') {
// Brotli-compressed Brotli dictionary
return FileType.binary;
}
final String base = path.basename(name);
if (base.startsWith('._')) {
bytes ??= reader();
if (matchesSignature(bytes, <int>[0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58])) {
if (matchesSignature(bytes, <int>[
0x00,
0x05,
0x16,
0x07,
0x00,
0x02,
0x00,
0x00,
0x4d,
0x61,
0x63,
0x20,
0x4f,
0x53,
0x20,
0x58,
])) {
return FileType.notPartOfBuild;
} // The ._* files in Mac OS X archives that gives icons and stuff
}
if (path.split(name).contains('cairo')) {
bytes ??= reader();
// "Copyright <latin1 copyright symbol> "
if (hasSubsequence(bytes, <int>[0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0xA9, 0x20], kMaxSize)) {
if (hasSubsequence(bytes, <int>[
0x43,
0x6f,
0x70,
0x79,
0x72,
0x69,
0x67,
0x68,
0x74,
0x20,
0xA9,
0x20,
], kMaxSize)) {
return FileType.latin1Text;
}
}
switch (base) {
// Build files
case 'DEPS': return FileType.text;
case 'MANIFEST': return FileType.text;
case 'DEPS':
return FileType.text;
case 'MANIFEST':
return FileType.text;
// Licenses
case 'COPYING': return FileType.text;
case 'LICENSE': return FileType.text;
case 'NOTICE.txt': return isMultiLicenseNotice(reader) ? FileType.binary : FileType.text;
case 'NOTICE': return FileType.text;
case 'COPYING':
return FileType.text;
case 'LICENSE':
return FileType.text;
case 'NOTICE.txt':
return isMultiLicenseNotice(reader) ? FileType.binary : FileType.text;
case 'NOTICE':
return FileType.text;
// Documentation
case 'Changes': return FileType.text;
case 'change.log': return FileType.text;
case 'ChangeLog': return FileType.text;
case 'CHANGES.0': return FileType.latin1Text;
case 'README': return FileType.text;
case 'TODO': return FileType.text;
case 'NEWS': return FileType.text;
case 'README.chromium': return FileType.text;
case 'README.flutter': return FileType.text;
case 'README.tests': return FileType.text;
case 'OWNERS': return FileType.text;
case 'AUTHORS': return FileType.text;
case 'Changes':
return FileType.text;
case 'change.log':
return FileType.text;
case 'ChangeLog':
return FileType.text;
case 'CHANGES.0':
return FileType.latin1Text;
case 'README':
return FileType.text;
case 'TODO':
return FileType.text;
case 'NEWS':
return FileType.text;
case 'README.chromium':
return FileType.text;
case 'README.flutter':
return FileType.text;
case 'README.tests':
return FileType.text;
case 'OWNERS':
return FileType.text;
case 'AUTHORS':
return FileType.text;
// Signatures (found in .jar files typically)
case 'CERT.RSA': return FileType.binary;
case 'ECLIPSE_.RSA': return FileType.binary;
case 'CERT.RSA':
return FileType.binary;
case 'ECLIPSE_.RSA':
return FileType.binary;
// Binary data files
case 'tzdata': return FileType.binary;
case 'compressed_atrace_data.txt': return FileType.binary;
case 'tzdata':
return FileType.binary;
case 'compressed_atrace_data.txt':
return FileType.binary;
// Source files that don't use UTF-8
case 'Messages_de_DE.properties': // has a few non-ASCII characters they forgot to escape (from gnu-libstdc++)
case 'mmx_blendtmp.h': // author name in comment contains latin1 (mesa)
@ -154,101 +227,192 @@ FileType identifyFile(String name, Reader reader) {
}
switch (path.extension(name)) {
// C/C++ code
case '.h': return FileType.text;
case '.c': return FileType.text;
case '.cc': return FileType.text;
case '.cpp': return FileType.text;
case '.inc': return FileType.text;
case '.h':
return FileType.text;
case '.c':
return FileType.text;
case '.cc':
return FileType.text;
case '.cpp':
return FileType.text;
case '.inc':
return FileType.text;
// Go code
case '.go': return FileType.text;
case '.go':
return FileType.text;
// ObjectiveC code
case '.m': return FileType.text;
case '.m':
return FileType.text;
// Assembler
case '.asm': return FileType.text;
case '.asm':
return FileType.text;
// Shell
case '.sh': return FileType.notPartOfBuild;
case '.bat': return FileType.notPartOfBuild;
case '.sh':
return FileType.notPartOfBuild;
case '.bat':
return FileType.notPartOfBuild;
// Build files
case '.ac': return FileType.notPartOfBuild;
case '.am': return FileType.notPartOfBuild;
case '.gn': return FileType.notPartOfBuild;
case '.gni': return FileType.notPartOfBuild;
case '.gyp': return FileType.notPartOfBuild;
case '.gypi': return FileType.notPartOfBuild;
case '.ac':
return FileType.notPartOfBuild;
case '.am':
return FileType.notPartOfBuild;
case '.gn':
return FileType.notPartOfBuild;
case '.gni':
return FileType.notPartOfBuild;
case '.gyp':
return FileType.notPartOfBuild;
case '.gypi':
return FileType.notPartOfBuild;
// Java code
case '.java': return FileType.text;
case '.jar': return FileType.zip; // Java package
case '.class': return FileType.binary; // compiled Java bytecode (usually found inside .jar archives)
case '.dex': return FileType.binary; // Dalvik Executable (usually found inside .jar archives)
case '.java':
return FileType.text;
case '.jar':
return FileType.zip; // Java package
case '.class':
return FileType.binary; // compiled Java bytecode (usually found inside .jar archives)
case '.dex':
return FileType.binary; // Dalvik Executable (usually found inside .jar archives)
// Dart code
case '.dart': return FileType.text;
case '.dill': return FileType.binary; // Compiled Dart code
case '.dart':
return FileType.text;
case '.dill':
return FileType.binary; // Compiled Dart code
// LLVM bitcode
case '.bc': return FileType.binary;
case '.bc':
return FileType.binary;
// Python code
case '.py':
bytes ??= reader();
// # -*- coding: Latin-1 -*-
if (matchesSignature(bytes, <int>[0x23, 0x20, 0x2d, 0x2a, 0x2d, 0x20, 0x63, 0x6f, 0x64,
0x69, 0x6e, 0x67, 0x3a, 0x20, 0x4c, 0x61, 0x74, 0x69,
0x6e, 0x2d, 0x31, 0x20, 0x2d, 0x2a, 0x2d])) {
if (matchesSignature(bytes, <int>[
0x23,
0x20,
0x2d,
0x2a,
0x2d,
0x20,
0x63,
0x6f,
0x64,
0x69,
0x6e,
0x67,
0x3a,
0x20,
0x4c,
0x61,
0x74,
0x69,
0x6e,
0x2d,
0x31,
0x20,
0x2d,
0x2a,
0x2d,
])) {
return FileType.latin1Text;
}
return FileType.text;
case '.pyc': return FileType.binary; // compiled Python bytecode
case '.pyc':
return FileType.binary; // compiled Python bytecode
// Machine code
case '.so': return FileType.binary; // ELF shared object
case '.xpt': return FileType.binary; // XPCOM Type Library
case '.so':
return FileType.binary; // ELF shared object
case '.xpt':
return FileType.binary; // XPCOM Type Library
// Graphics code
case '.glsl': return FileType.text;
case '.spvasm': return FileType.text;
case '.glsl':
return FileType.text;
case '.spvasm':
return FileType.text;
// Documentation
case '.md': return FileType.text;
case '.txt': return FileType.text;
case '.html': return FileType.text;
case '.md':
return FileType.text;
case '.txt':
return FileType.text;
case '.html':
return FileType.text;
// Fonts
case '.ttf': return FileType.binary; // TrueType Font
case '.ttf':
return FileType.binary; // TrueType Font
case '.ttcf': // (mac)
case '.ttc': return FileType.binary; // TrueType Collection (windows)
case '.woff': return FileType.binary; // Web Open Font Format
case '.otf': return FileType.binary; // OpenType Font
case '.ttc':
return FileType.binary; // TrueType Collection (windows)
case '.woff':
return FileType.binary; // Web Open Font Format
case '.otf':
return FileType.binary; // OpenType Font
// Graphics formats
case '.gif': return FileType.binary; // GIF
case '.png': return FileType.binary; // PNG
case '.tga': return FileType.binary; // Truevision TGA (TARGA)
case '.dng': return FileType.binary; // Digial Negative (Adobe RAW format)
case '.gif':
return FileType.binary; // GIF
case '.png':
return FileType.binary; // PNG
case '.tga':
return FileType.binary; // Truevision TGA (TARGA)
case '.dng':
return FileType.binary; // Digial Negative (Adobe RAW format)
case '.jpg':
case '.jpeg': return FileType.binary; // JPEG
case '.ico': return FileType.binary; // Windows icon format
case '.icns': return FileType.binary; // macOS icon format
case '.bmp': return FileType.binary; // Windows bitmap format
case '.wbmp': return FileType.binary; // Wireless bitmap format
case '.webp': return FileType.binary; // WEBP
case '.pdf': return FileType.binary; // PDF
case '.emf': return FileType.binary; // Windows enhanced metafile format
case '.skp': return FileType.binary; // Skia picture format
case '.mskp': return FileType.binary; // Skia picture format
case '.spv': return FileType.binary; // SPIR-V
case '.jpeg':
return FileType.binary; // JPEG
case '.ico':
return FileType.binary; // Windows icon format
case '.icns':
return FileType.binary; // macOS icon format
case '.bmp':
return FileType.binary; // Windows bitmap format
case '.wbmp':
return FileType.binary; // Wireless bitmap format
case '.webp':
return FileType.binary; // WEBP
case '.pdf':
return FileType.binary; // PDF
case '.emf':
return FileType.binary; // Windows enhanced metafile format
case '.skp':
return FileType.binary; // Skia picture format
case '.mskp':
return FileType.binary; // Skia picture format
case '.spv':
return FileType.binary; // SPIR-V
// Videos
case '.ogg': return FileType.binary; // Ogg media
case '.mp4': return FileType.binary; // MPEG media
case '.ts': return FileType.binary; // MPEG2 transport stream
case '.ogg':
return FileType.binary; // Ogg media
case '.mp4':
return FileType.binary; // MPEG media
case '.ts':
return FileType.binary; // MPEG2 transport stream
// Other binary files
case '.raw': return FileType.binary; // raw audio or graphical data
case '.bin': return FileType.binary; // some sort of binary data
case '.rsc': return FileType.binary; // some sort of resource data
case '.arsc': return FileType.binary; // Android compiled resources
case '.apk': return FileType.zip; // Android Package
case '.crx': return FileType.binary; // Chrome extension
case '.keystore': return FileType.binary;
case '.icc': return FileType.binary; // Color profile
case '.swp': return FileType.binary; // Vim swap file
case '.bfbs': return FileType.binary; // Flatbuffers Binary Schema
case '.raw':
return FileType.binary; // raw audio or graphical data
case '.bin':
return FileType.binary; // some sort of binary data
case '.rsc':
return FileType.binary; // some sort of resource data
case '.arsc':
return FileType.binary; // Android compiled resources
case '.apk':
return FileType.zip; // Android Package
case '.crx':
return FileType.binary; // Chrome extension
case '.keystore':
return FileType.binary;
case '.icc':
return FileType.binary; // Color profile
case '.swp':
return FileType.binary; // Vim swap file
case '.bfbs':
return FileType.binary; // Flatbuffers Binary Schema
// Archives
case '.zip': return FileType.zip; // ZIP
case '.tar': return FileType.tar; // Tar
case '.gz': return FileType.gz; // GZip
case '.bzip2': return FileType.bzip2; // BZip2
case '.zip':
return FileType.zip; // ZIP
case '.tar':
return FileType.tar; // Tar
case '.gz':
return FileType.gz; // GZip
case '.bzip2':
return FileType.bzip2; // BZip2
// Image file types from the Fuchsia SDK.
case '.blk':
case '.vboot':
@ -340,7 +504,24 @@ FileType identifyFile(String name, Reader reader) {
if (matchesSignature(bytes, <int>[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0a])) {
return FileType.binary;
} // PNG
if (matchesSignature(bytes, <int>[0x58, 0x50, 0x43, 0x4f, 0x4d, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x4c, 0x69, 0x62, 0x0d, 0x0a, 0x1a])) {
if (matchesSignature(bytes, <int>[
0x58,
0x50,
0x43,
0x4f,
0x4d,
0x0a,
0x54,
0x79,
0x70,
0x65,
0x4c,
0x69,
0x62,
0x0d,
0x0a,
0x1a,
])) {
return FileType.binary;
} // XPCOM Type Library
if (matchesSignature(bytes, <int>[0x23, 0x21])) {
@ -356,7 +537,6 @@ String _normalize(String fileContents) {
return fileContents;
}
// INTERFACE
// base class
@ -423,7 +603,7 @@ abstract class Directory extends IoNode {
}
// interface
abstract class Link extends IoNode { }
abstract class Link extends IoNode {}
mixin ZipFile on File implements Directory {
ArchiveDirectory? _root;
@ -462,7 +642,8 @@ mixin GZipFile on File implements Directory {
Iterable<IoNode> get walk sync* {
try {
final String innerName = path.basenameWithoutExtension(fullName);
_data ??= InMemoryFile.parse('$fullName!$innerName', a.GZipDecoder().decodeBytes(readBytes()!))!;
_data ??=
InMemoryFile.parse('$fullName!$innerName', a.GZipDecoder().decodeBytes(readBytes()!))!;
if (_data != null) {
yield _data!;
}
@ -480,7 +661,8 @@ mixin BZip2File on File implements Directory {
Iterable<IoNode> get walk sync* {
try {
final String innerName = path.basenameWithoutExtension(fullName);
_data ??= InMemoryFile.parse('$fullName!$innerName', a.BZip2Decoder().decodeBytes(readBytes()!))!;
_data ??=
InMemoryFile.parse('$fullName!$innerName', a.BZip2Decoder().decodeBytes(readBytes()!))!;
if (_data != null) {
yield _data!;
}
@ -491,7 +673,6 @@ mixin BZip2File on File implements Directory {
}
}
// FILESYSTEM IMPLEMENTATIoN
class FileSystemDirectory extends IoNode implements Directory {
@ -527,14 +708,22 @@ class FileSystemDirectory extends IoNode implements Directory {
final io.File fileEntity = entity as io.File;
if (fileEntity.lengthSync() > 0) {
switch (identifyFile(fileEntity.path, () => _readBytes(fileEntity))) {
case FileType.binary: yield FileSystemFile(fileEntity);
case FileType.zip: yield FileSystemZipFile(fileEntity);
case FileType.tar: yield FileSystemTarFile(fileEntity);
case FileType.gz: yield FileSystemGZipFile(fileEntity);
case FileType.bzip2: yield FileSystemBZip2File(fileEntity);
case FileType.text: yield FileSystemUTF8TextFile(fileEntity);
case FileType.latin1Text: yield FileSystemLatin1TextFile(fileEntity);
case FileType.notPartOfBuild: break; // ignore this file
case FileType.binary:
yield FileSystemFile(fileEntity);
case FileType.zip:
yield FileSystemZipFile(fileEntity);
case FileType.tar:
yield FileSystemTarFile(fileEntity);
case FileType.gz:
yield FileSystemGZipFile(fileEntity);
case FileType.bzip2:
yield FileSystemBZip2File(fileEntity);
case FileType.text:
yield FileSystemUTF8TextFile(fileEntity);
case FileType.latin1Text:
yield FileSystemLatin1TextFile(fileEntity);
case FileType.notPartOfBuild:
break; // ignore this file
}
}
}
@ -595,7 +784,6 @@ class FileSystemBZip2File extends FileSystemFile with BZip2File {
FileSystemBZip2File(super.file);
}
// ARCHIVES
class ArchiveDirectory extends IoNode implements Directory {
@ -613,22 +801,32 @@ class ArchiveDirectory extends IoNode implements Directory {
void _add(a.ArchiveFile entry, List<String> remainingPath) {
if (remainingPath.length > 1) {
final String subdirectoryName = remainingPath.removeAt(0);
_subdirectories.putIfAbsent(
subdirectoryName,
() => ArchiveDirectory('$fullName/$subdirectoryName', subdirectoryName)
)._add(entry, remainingPath);
_subdirectories
.putIfAbsent(
subdirectoryName,
() => ArchiveDirectory('$fullName/$subdirectoryName', subdirectoryName),
)
._add(entry, remainingPath);
} else {
if (entry.size > 0) {
final String entryFullName = '$fullName/${path.basename(entry.name)}';
switch (identifyFile(entry.name, () => entry.content as List<int>)) {
case FileType.binary: _files.add(ArchiveFile(entryFullName, entry));
case FileType.zip: _files.add(ArchiveZipFile(entryFullName, entry));
case FileType.tar: _files.add(ArchiveTarFile(entryFullName, entry));
case FileType.gz: _files.add(ArchiveGZipFile(entryFullName, entry));
case FileType.bzip2: _files.add(ArchiveBZip2File(entryFullName, entry));
case FileType.text: _files.add(ArchiveUTF8TextFile(entryFullName, entry));
case FileType.latin1Text: _files.add(ArchiveLatin1TextFile(entryFullName, entry));
case FileType.notPartOfBuild: break; // ignore this file
case FileType.binary:
_files.add(ArchiveFile(entryFullName, entry));
case FileType.zip:
_files.add(ArchiveZipFile(entryFullName, entry));
case FileType.tar:
_files.add(ArchiveTarFile(entryFullName, entry));
case FileType.gz:
_files.add(ArchiveGZipFile(entryFullName, entry));
case FileType.bzip2:
_files.add(ArchiveBZip2File(entryFullName, entry));
case FileType.text:
_files.add(ArchiveUTF8TextFile(entryFullName, entry));
case FileType.latin1Text:
_files.add(ArchiveLatin1TextFile(entryFullName, entry));
case FileType.notPartOfBuild:
break; // ignore this file
}
}
}
@ -692,7 +890,6 @@ class ArchiveBZip2File extends ArchiveFile with BZip2File {
ArchiveBZip2File(super.fullName, super.file);
}
// IN-MEMORY FILES (e.g. contents of GZipped files)
class InMemoryFile extends IoNode implements File {
@ -703,14 +900,22 @@ class InMemoryFile extends IoNode implements File {
return null;
}
switch (identifyFile(fullName, () => bytes)) {
case FileType.binary: return InMemoryFile(fullName, bytes);
case FileType.zip: return InMemoryZipFile(fullName, bytes);
case FileType.tar: return InMemoryTarFile(fullName, bytes);
case FileType.gz: return InMemoryGZipFile(fullName, bytes);
case FileType.bzip2: return InMemoryBZip2File(fullName, bytes);
case FileType.text: return InMemoryUTF8TextFile(fullName, bytes);
case FileType.latin1Text: return InMemoryLatin1TextFile(fullName, bytes);
case FileType.notPartOfBuild: break; // ignore this file
case FileType.binary:
return InMemoryFile(fullName, bytes);
case FileType.zip:
return InMemoryZipFile(fullName, bytes);
case FileType.tar:
return InMemoryTarFile(fullName, bytes);
case FileType.gz:
return InMemoryGZipFile(fullName, bytes);
case FileType.bzip2:
return InMemoryBZip2File(fullName, bytes);
case FileType.text:
return InMemoryUTF8TextFile(fullName, bytes);
case FileType.latin1Text:
return InMemoryLatin1TextFile(fullName, bytes);
case FileType.notPartOfBuild:
break; // ignore this file
}
assert(false);
return null;

View File

@ -38,7 +38,8 @@ bool _stripIndentation(List<String> lines) {
} else {
prefix = leadingDecorations.matchAsPrefix(lines.first)?.group(0);
}
if (prefix != null && lines.skip(1).every((String line) => line.startsWith(prefix!) || prefix.startsWith(line))) {
if (prefix != null &&
lines.skip(1).every((String line) => line.startsWith(prefix!) || prefix.startsWith(line))) {
final int prefixLength = prefix.length;
for (int index = 0; index < lines.length; index += 1) {
final String line = lines[index];
@ -168,7 +169,8 @@ String stripAsciiArt(String input) {
for (final List<String> image in asciiArtImages) {
assert(image.isNotEmpty);
// Look for the image starting on each line.
search: for (int index = 0; index < lines.length - image.length; index += 1) {
search:
for (int index = 0; index < lines.length - image.length; index += 1) {
final int x = lines[index].indexOf(image[0]);
if (x >= 0) {
int width = image[0].length;
@ -186,7 +188,8 @@ String stripAsciiArt(String input) {
final String text = lines[index + imageLine];
assert(text.length > x);
if (text.length >= x + width) {
lines[index + imageLine] = text.substring(0, x) + text.substring(x + width, text.length);
lines[index + imageLine] =
text.substring(0, x) + text.substring(x + width, text.length);
} else {
lines[index + imageLine] = text.substring(0, x);
}

File diff suppressed because it is too large Load Diff

View File

@ -187,7 +187,16 @@ class _RepositoryGeneralSingleLicenseFile extends _RepositorySingleLicenseFile {
// (e.g. having the copyright for one above the terms for the other and so on).
class _RepositoryOpaqueLicenseFile extends _RepositorySingleLicenseFile {
_RepositoryOpaqueLicenseFile(_RepositoryDirectory parent, fs.TextFile io)
: super(parent, io, License.unique(io.readString(), LicenseType.unknown, origin: io.fullName, yesWeKnowWhatItLooksLikeButItIsNot: true));
: super(
parent,
io,
License.unique(
io.readString(),
LicenseType.unknown,
origin: io.fullName,
yesWeKnowWhatItLooksLikeButItIsNot: true,
),
);
}
class _RepositoryReadmeIjgFile extends _RepositorySingleLicenseFile {
@ -197,7 +206,8 @@ class _RepositoryReadmeIjgFile extends _RepositorySingleLicenseFile {
// The message we are required to include in our output.
//
// We include it by just including the whole license.
static const String _message = 'this software is based in part on the work of the Independent JPEG Group';
static const String _message =
'this software is based in part on the work of the Independent JPEG Group';
// The license text that says we should output _message.
//
@ -212,7 +222,9 @@ class _RepositoryReadmeIjgFile extends _RepositorySingleLicenseFile {
r'unaltered; and any additions, deletions, or changes to the original files\n'
r'must be clearly indicated in accompanying documentation\.\n'
r'\(2\) If only executable code is distributed, then the accompanying\n'
r'documentation must state that "' '${_message.replaceAll(" ", "[ \n]+")}' r'"\.\n'
r'documentation must state that "'
'${_message.replaceAll(" ", "[ \n]+")}'
r'"\.\n'
r'\(3\) Permission for use of this software is granted only if the user accepts\n'
r'full responsibility for any undesirable consequences; the authors accept\n'
r'NO LIABILITY for damages of any kind\.\n',
@ -239,9 +251,7 @@ class _RepositoryDartLicenseFile extends _RepositorySingleLicenseFile {
_RepositoryDartLicenseFile(_RepositoryDirectory parent, fs.TextFile io)
: super(parent, io, _parseLicense(io));
static final RegExp _pattern = RegExp(
r'(Copyright (?:.|\n)+)$',
);
static final RegExp _pattern = RegExp(r'(Copyright (?:.|\n)+)$');
static License _parseLicense(fs.TextFile io) {
final Match? match = _pattern.firstMatch(io.readString());
@ -290,7 +300,7 @@ class _RepositoryLibJpegTurboLicenseFile extends _RepositoryLicenseFile {
r'- The zlib License, which is listed in \[simd/jsimdext\.inc\]\(simd/jsimdext\.inc\)\n'
r'\n'
r' This license is a subset of the other two, and it covers the libjpeg-turbo\n'
r' SIMD extensions\.\n'
r' SIMD extensions\.\n',
);
static void _parseLicense(fs.TextFile io) {
@ -318,10 +328,13 @@ class _RepositoryLibJpegTurboLicenseFile extends _RepositoryLicenseFile {
@override
List<License> get licenses {
if (_licenses == null) {
final _RepositoryLicenseFile readme = parent!.getChildByName('README.ijg') as _RepositoryReadmeIjgFile;
final _RepositorySourceFile main = parent!.getChildByName('turbojpeg.c') as _RepositorySourceFile;
final _RepositoryLicenseFile readme =
parent!.getChildByName('README.ijg') as _RepositoryReadmeIjgFile;
final _RepositorySourceFile main =
parent!.getChildByName('turbojpeg.c') as _RepositorySourceFile;
final _RepositoryDirectory simd = parent!.getChildByName('simd') as _RepositoryDirectory;
final _RepositorySourceFile zlib = simd.getChildByName('jsimdext.inc') as _RepositorySourceFile;
final _RepositorySourceFile zlib =
simd.getChildByName('jsimdext.inc') as _RepositorySourceFile;
_licenses = <License>[];
_licenses!.addAll(readme.licenses);
_licenses!.add(main.extractInternalLicense());
@ -332,8 +345,7 @@ class _RepositoryLibJpegTurboLicenseFile extends _RepositoryLicenseFile {
}
class _RepositoryFreetypeLicenseFile extends _RepositoryLicenseFile {
_RepositoryFreetypeLicenseFile(super.parent, super.io)
: _target = _parseLicense(io);
_RepositoryFreetypeLicenseFile(super.parent, super.io) : _target = _parseLicense(io);
static final RegExp _pattern = RegExp(
r'FREETYPE LICENSES\n'
@ -376,7 +388,7 @@ class _RepositoryFreetypeLicenseFile extends _RepositoryLicenseFile {
r'The MD5 checksum support \(only used for debugging in development\n'
r'builds\) is in the public domain\.\n'
r'\n*'
r'--- end of LICENSE\.TXT ---\n*$'
r'--- end of LICENSE\.TXT ---\n*$',
);
static String _parseLicense(fs.TextFile io) {
@ -426,7 +438,10 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
throw 'could not parse ICU license file';
}
const int groupCount = 22;
assert(match.groupCount == groupCount, 'ICU: expected $groupCount groups, but got ${match.groupCount}');
assert(
match.groupCount == groupCount,
'ICU: expected $groupCount groups, but got ${match.groupCount}',
);
const int timeZoneGroup = 18;
if (match.group(timeZoneGroup)!.contains(copyrightMentionPattern)) {
throw 'ICU: unexpected copyright in time zone database group\n:${match.group(timeZoneGroup)}';
@ -436,21 +451,24 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
}
const int gplGroup1 = 20;
const int gplGroup2 = 21;
if (!match.group(gplGroup1)!.contains(gplExceptionExplanation1) || !match.group(gplGroup2)!.contains(gplExceptionExplanation2)) {
if (!match.group(gplGroup1)!.contains(gplExceptionExplanation1) ||
!match.group(gplGroup2)!.contains(gplExceptionExplanation2)) {
throw 'ICU: did not find GPL exception in GPL-licensed files';
}
const Set<int> skippedGroups = <int>{ timeZoneGroup, gplGroup1, gplGroup2 };
const Set<int> skippedGroups = <int>{timeZoneGroup, gplGroup1, gplGroup2};
return _RepositoryIcuLicenseFile._(
parent,
io,
License.template(match.group(2)!, LicenseType.bsd, origin: io.fullName),
License.fromMultipleBlocks(
match.groups(
Iterable<int>
.generate(groupCount, (int index) => index + 1)
.where((int index) => !skippedGroups.contains(index))
.toList()
).cast<String>(),
match
.groups(
Iterable<int>.generate(
groupCount,
(int index) => index + 1,
).where((int index) => !skippedGroups.contains(index)).toList(),
)
.cast<String>(),
LicenseType.icu,
origin: io.fullName,
yesWeKnowWhatItLooksLikeButItIsNot: true,
@ -458,8 +476,12 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
);
}
_RepositoryIcuLicenseFile._(_RepositoryDirectory parent, fs.TextFile io, this.template, License license)
: super(parent, io, license);
_RepositoryIcuLicenseFile._(
_RepositoryDirectory parent,
fs.TextFile io,
this.template,
License license,
) : super(parent, io, license);
// Every paragraph of the license is mentioned. All newlines are disregarded [\n+].
static final RegExp _pattern = RegExp(
@ -563,29 +585,29 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
);
static const String gplExceptionExplanation1 =
'As a special exception to the GNU General Public License, if you\n'
'distribute this file as part of a program that contains a\n'
'configuration script generated by Autoconf, you may include it under\n'
'the same distribution terms that you use for the rest of that\n'
'program.\n'
'\n'
'\n'
'(The condition for the exception is fulfilled because\n'
'ICU4C includes a configuration script generated by Autoconf,\n'
'namely the `configure` script.)';
'As a special exception to the GNU General Public License, if you\n'
'distribute this file as part of a program that contains a\n'
'configuration script generated by Autoconf, you may include it under\n'
'the same distribution terms that you use for the rest of that\n'
'program.\n'
'\n'
'\n'
'(The condition for the exception is fulfilled because\n'
'ICU4C includes a configuration script generated by Autoconf,\n'
'namely the `configure` script.)';
static const String gplExceptionExplanation2 =
'As a special exception to the GNU General Public License, if you\n'
'distribute this file as part of a program that contains a\n'
'configuration script generated by Autoconf, you may include it under\n'
'the same distribution terms that you use for the rest of that\n'
'program. This Exception is an additional permission under section 7\n'
'of the GNU General Public License, version 3 ("GPLv3").\n'
'\n'
'\n'
'(The condition for the exception is fulfilled because\n'
'ICU4C includes a configuration script generated by Autoconf,\n'
'namely the `configure` script.)';
'As a special exception to the GNU General Public License, if you\n'
'distribute this file as part of a program that contains a\n'
'configuration script generated by Autoconf, you may include it under\n'
'the same distribution terms that you use for the rest of that\n'
'program. This Exception is an additional permission under section 7\n'
'of the GNU General Public License, version 3 ("GPLv3").\n'
'\n'
'\n'
'(The condition for the exception is fulfilled because\n'
'ICU4C includes a configuration script generated by Autoconf,\n'
'namely the `configure` script.)';
// Fixes an error in the license's formatting that our reformatter wouldn't be
// able to figure out on its own and which would otherwise completely mess up
@ -609,8 +631,7 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
}
class _RepositoryCxxStlDualLicenseFile extends _RepositoryLicenseFile {
_RepositoryCxxStlDualLicenseFile(super.parent, super.io)
: _licenses = _parseLicenses(io);
_RepositoryCxxStlDualLicenseFile(super.parent, super.io) : _licenses = _parseLicenses(io);
static final RegExp _pattern = RegExp(
r'^'
@ -750,8 +771,7 @@ class _RepositoryCxxStlDualLicenseFile extends _RepositoryLicenseFile {
}
class _RepositoryKhronosLicenseFile extends _RepositoryLicenseFile {
_RepositoryKhronosLicenseFile(super.parent, super.io)
: _licenses = _parseLicenses(io);
_RepositoryKhronosLicenseFile(super.parent, super.io) : _licenses = _parseLicenses(io);
static final RegExp _pattern = RegExp(
r'^(Copyright .+?)\n'
@ -852,16 +872,21 @@ class _RepositoryFuchsiaSdkLinuxLicenseFile extends _RepositorySingleLicenseFile
_RepositoryFuchsiaSdkLinuxLicenseFile(_RepositoryDirectory parent, fs.TextFile io)
: super(parent, io, _parseLicense(io));
static const String _pattern = 'The majority of files in this project use the Apache 2.0 License.\n'
'There are a few exceptions and their license can be found in the source.\n'
'Any license deviations from Apache 2.0 are "more permissive" licenses.\n';
static const String _pattern =
'The majority of files in this project use the Apache 2.0 License.\n'
'There are a few exceptions and their license can be found in the source.\n'
'Any license deviations from Apache 2.0 are "more permissive" licenses.\n';
static License _parseLicense(fs.TextFile io) {
final String body = io.readString();
if (!body.startsWith(_pattern)) {
throw 'unexpected Fuchsia license file contents';
}
return License.fromBodyAndType(body.substring(_pattern.length), LicenseType.apache, origin: io.fullName);
return License.fromBodyAndType(
body.substring(_pattern.length),
LicenseType.apache,
origin: io.fullName,
);
}
}
@ -875,13 +900,13 @@ class _RepositoryVulkanApacheLicenseFile extends _RepositorySingleLicenseFile {
: super(parent, io, _parseLicense(io));
static const String _prefix =
'The majority of files in this project use the Apache 2.0 License.\n'
'There are a few exceptions and their license can be found in the source.\n'
'Any license deviations from Apache 2.0 are "more permissive" licenses.\n'
"Any file without a license in it's source defaults to the repository Apache 2.0 License.\n"
'\n'
'===========================================================================================\n'
'\n';
'The majority of files in this project use the Apache 2.0 License.\n'
'There are a few exceptions and their license can be found in the source.\n'
'Any license deviations from Apache 2.0 are "more permissive" licenses.\n'
"Any file without a license in it's source defaults to the repository Apache 2.0 License.\n"
'\n'
'===========================================================================================\n'
'\n';
static License _parseLicense(fs.TextFile io) {
final String body = io.readString();
@ -892,7 +917,6 @@ class _RepositoryVulkanApacheLicenseFile extends _RepositorySingleLicenseFile {
}
}
// DIRECTORIES
typedef _Constructor = _RepositoryFile Function(_RepositoryDirectory parent, fs.TextFile);
@ -912,7 +936,7 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
List<_RepositoryDirectory> get subdirectories => _subdirectories;
final Map<String,_RepositoryEntry> _childrenByName = <String,_RepositoryEntry>{};
final Map<String, _RepositoryEntry> _childrenByName = <String, _RepositoryEntry>{};
void crawl() {
for (final fs.IoNode entry in ioDirectory.walk) {
@ -987,7 +1011,10 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
}
// the bit at the beginning excludes files like "license.py".
static final RegExp _licenseNamePattern = RegExp(r'^(?!.*\.py$)(?!.*(?:no|update)-copyright)(?!.*mh-bsd-gcc).*\b_*(?:license(?!\.html)|copying|copyright|notice|l?gpl|GPLv2|bsd|mit|mpl?|ftl|Apache)_*\b', caseSensitive: false);
static final RegExp _licenseNamePattern = RegExp(
r'^(?!.*\.py$)(?!.*(?:no|update)-copyright)(?!.*mh-bsd-gcc).*\b_*(?:license(?!\.html)|copying|copyright|notice|l?gpl|GPLv2|bsd|mit|mpl?|ftl|Apache)_*\b',
caseSensitive: false,
);
static const Map<String, _Constructor> _specialCaseFiles = <String, _Constructor>{
'/flutter/third_party/boringssl/src/LICENSE': _RepositoryOpenSSLLicenseFile.new,
@ -1002,7 +1029,8 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
'/flutter/third_party/libpng/LICENSE': _RepositoryLibPngLicenseFile.new,
'/flutter/third_party/rapidjson/LICENSE': _RepositoryOpaqueLicenseFile.new,
'/flutter/third_party/rapidjson/license.txt': _RepositoryOpaqueLicenseFile.new,
'/flutter/third_party/vulkan-deps/vulkan-validation-layers/src/LICENSE.txt': _RepositoryVulkanApacheLicenseFile.new,
'/flutter/third_party/vulkan-deps/vulkan-validation-layers/src/LICENSE.txt':
_RepositoryVulkanApacheLicenseFile.new,
'/fuchsia/sdk/linux/LICENSE.vulkan': _RepositoryFuchsiaSdkLinuxLicenseFile.new,
'/fuchsia/sdk/mac/LICENSE.vulkan': _RepositoryFuchsiaSdkLinuxLicenseFile.new,
'/third_party/khronos/LICENSE': _RepositoryKhronosLicenseFile.new,
@ -1023,7 +1051,9 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
return _RepositoryBinaryFile(this, entry as fs.File);
}
int get count => _files.length + _subdirectories.fold<int>(0, (int count, _RepositoryDirectory child) => count + child.count);
int get count =>
_files.length +
_subdirectories.fold<int>(0, (int count, _RepositoryDirectory child) => count + child.count);
bool _canGoUp() {
assert(parent != null || isLicenseRoot);
@ -1063,13 +1093,14 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
/// Searches the current directory for licenses of the specified type.
License? _localLicenseWithType(LicenseType type) {
final List<License> licenses = _licenses.expand((_RepositoryLicenseFile license) {
final License? result = license.licenseOfType(type);
if (result != null) {
return <License>[result];
}
return const <License>[];
}).toList();
final List<License> licenses =
_licenses.expand((_RepositoryLicenseFile license) {
final License? result = license.licenseOfType(type);
if (result != null) {
return <License>[result];
}
return const <License>[];
}).toList();
if (licenses.length > 1) {
print('unexpectedly found multiple matching licenses in $name of type $type');
return null;
@ -1083,8 +1114,11 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
/// Searches all subdirectories (depth-first) for a license of the specified type.
License? _fullWalkDownForLicenseWithType(LicenseType type) {
for (final _RepositoryDirectory directory in _subdirectories) {
if (directory._canGoUp()) { // avoid crawling into other license scopes
final License? result = directory._localLicenseWithType(type) ?? directory._fullWalkDownForLicenseWithType(type);
if (directory._canGoUp()) {
// avoid crawling into other license scopes
final License? result =
directory._localLicenseWithType(type) ??
directory._fullWalkDownForLicenseWithType(type);
if (result != null) {
return result;
}
@ -1108,13 +1142,13 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
if (_canGoUp()) {
return parent!.nearestLicenseWithName(name, authors: authors);
}
return _fullWalkDownForLicenseWithName(name, authors: authors)
?? (authors != null ? parent?._fullWalkUpForLicenseWithName(name, authors: authors) : null);
return _fullWalkDownForLicenseWithName(name, authors: authors) ??
(authors != null ? parent?._fullWalkUpForLicenseWithName(name, authors: authors) : null);
});
return result;
}
License? _localLicenseWithName(String name, { String? authors }) {
License? _localLicenseWithName(String name, {String? authors}) {
final _RepositoryEntry? entry = _childrenByName[name];
License? license;
if (entry is _RepositoryLicensedFile) {
@ -1132,7 +1166,7 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
return license;
}
License? _fullWalkUpForLicenseWithName(String name, { required String authors }) {
License? _fullWalkUpForLicenseWithName(String name, {required String authors}) {
// When looking for a license specific to certain authors, we want to walk
// to the top of the local license root, then from there check all the
// ancestors and all the descendants.
@ -1143,14 +1177,16 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
// Authors license is at the root, and is sometimes mentioned in various
// files deep inside third party directories).
return _localLicenseWithName(name, authors: authors) ??
parent?._fullWalkUpForLicenseWithName(name, authors: authors);
parent?._fullWalkUpForLicenseWithName(name, authors: authors);
}
License? _fullWalkDownForLicenseWithName(String name, { String? authors }) {
License? _fullWalkDownForLicenseWithName(String name, {String? authors}) {
for (final _RepositoryDirectory directory in _subdirectories) {
if (directory._canGoUp()) { // avoid crawling into other license scopes
final License? result = directory._localLicenseWithName(name, authors: authors)
?? directory._fullWalkDownForLicenseWithName(name, authors: authors);
if (directory._canGoUp()) {
// avoid crawling into other license scopes
final License? result =
directory._localLicenseWithName(name, authors: authors) ??
directory._fullWalkDownForLicenseWithName(name, authors: authors);
if (result != null) {
return result;
}
@ -1246,8 +1282,10 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
/// this directory tree.
Future<String> get signature async {
final List<_RepositoryLicensedFile> allFiles = _signatureFiles.toList();
allFiles.sort((_RepositoryLicensedFile a, _RepositoryLicensedFile b) =>
a.io.fullName.compareTo(b.io.fullName));
allFiles.sort(
(_RepositoryLicensedFile a, _RepositoryLicensedFile b) =>
a.io.fullName.compareTo(b.io.fullName),
);
final crypto.Digest digest = await crypto.md5.bind(_signatureStream(allFiles)).single;
return digest.bytes.map((int e) => e.toRadixString(16).padLeft(2, '0')).join();
}
@ -1277,12 +1315,19 @@ class _RepositoryReachOutFile extends _RepositoryLicensedFile {
directory = directory.parent;
index -= 1;
}
return directory!.nearestLicensesFor(name).map((License license) => license.assignLicenses(io.fullName, parent!));
return directory!
.nearestLicensesFor(name)
.map((License license) => license.assignLicenses(io.fullName, parent!));
}
}
class _RepositoryReachOutDirectory extends _RepositoryDirectory {
_RepositoryReachOutDirectory(_RepositoryDirectory super.parent, super.io, this.reachOutFilenames, this.offset);
_RepositoryReachOutDirectory(
_RepositoryDirectory super.parent,
super.io,
this.reachOutFilenames,
this.offset,
);
final Set<String> reachOutFilenames;
final int offset;
@ -1356,13 +1401,25 @@ class _RepositoryInjaJsonFile extends _RepositorySourceFile {
throw '${io.fullName} has changed contents.';
}
final String license = match.group(3)!;
_internalLicenses = match.groups(const <int>[ 2, 6, 8 ]).map<License>((String? copyright) {
assert(copyright!.contains('Copyright'));
return License.fromCopyrightAndLicense(copyright!, license, LicenseType.mit, origin: io.fullName);
}).toList();
assert(!match.groups(const <int>[ 1, 4, 5, 7, 9, 10, 11]).any((String? text) => text!.contains(copyrightMentionPattern)));
_internalLicenses =
match.groups(const <int>[2, 6, 8]).map<License>((String? copyright) {
assert(copyright!.contains('Copyright'));
return License.fromCopyrightAndLicense(
copyright!,
license,
LicenseType.mit,
origin: io.fullName,
);
}).toList();
assert(
!match
.groups(const <int>[1, 4, 5, 7, 9, 10, 11])
.any((String? text) => text!.contains(copyrightMentionPattern)),
);
}
return _internalLicenses!.map((License license) => license.assignLicenses(io.fullName, parent!));
return _internalLicenses!.map(
(License license) => license.assignLicenses(io.fullName, parent!),
);
}
}
@ -1390,8 +1447,10 @@ class _EngineSrcDirectory extends _RepositoryDirectory {
@override
bool shouldRecurse(fs.IoNode entry) {
return entry.name != 'third_party' // all third_party components have been moved to flutter/third_party
&& super.shouldRecurse(entry);
return entry.name !=
'third_party' // all third_party components have been moved to flutter/third_party
&&
super.shouldRecurse(entry);
}
@override
@ -1520,7 +1579,10 @@ class _RepositoryFallbackRootCertificatesDirectory extends _RepositoryDirectory
@override
String get officialSourceLocation {
final system.ProcessResult result = system.Process.runSync('git', <String>['rev-parse', 'HEAD'], workingDirectory: '$this');
final system.ProcessResult result = system.Process.runSync('git', <String>[
'rev-parse',
'HEAD',
], workingDirectory: '$this');
if (result.exitCode != 0) {
throw 'Failed to run "git rev-parse HEAD"; got non-zero exit code ${result.exitCode}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}';
}
@ -1538,7 +1600,7 @@ class _RepositoryZLibDirectory extends _RepositoryDirectory {
// exact same license text as in LICENSE.
@override
License? nearestLicenseWithName(String name, { String? authors }) {
License? nearestLicenseWithName(String name, {String? authors}) {
if (name == 'MiniZip_info.txt') {
return super.nearestLicenseWithName('LICENSE', authors: authors)!;
}
@ -1663,7 +1725,9 @@ class _RepositoryBoringSSLDirectory extends _RepositoryDirectory {
@override
License? nearestLicenseWithName(String name, {String? authors}) {
assert(!src._canGoUp());
final License? result = super.nearestLicenseWithName(name, authors: authors) ?? src.nearestLicenseWithName(name, authors: authors);
final License? result =
super.nearestLicenseWithName(name, authors: authors) ??
src.nearestLicenseWithName(name, authors: authors);
return result;
}
@ -1713,9 +1777,13 @@ class _RepositoryFlutterThirdPartyDirectory extends _RepositoryGenericThirdParty
@override
bool shouldRecurse(fs.IoNode entry) {
return entry.name != 'skia' // handled as a virtual directory of the root
&& entry.name != 'dart' // handled as a virtual directory of the root
&& super.shouldRecurse(entry);
return entry.name !=
'skia' // handled as a virtual directory of the root
&&
entry.name !=
'dart' // handled as a virtual directory of the root
&&
super.shouldRecurse(entry);
}
@override
@ -1756,7 +1824,6 @@ class _RepositoryGpuShimDirectory extends _RepositoryDirectory {
String get libraryName => 'engine';
}
/// The license tool directory.
///
/// This is a special-case root node that is not used for license aggregation,
@ -1772,13 +1839,14 @@ class _RepositoryFlutterLicenseToolDirectory extends _RepositoryDirectory {
}
}
// BOOTSTRAPPING LOGIC
fs.Directory? findChildDirectory(fs.Directory parent, String name) {
return parent.walk.firstWhereOrNull( // from IterableExtension in package:collection
(fs.IoNode child) => child.name == name,
) as fs.Directory?;
return parent.walk.firstWhereOrNull(
// from IterableExtension in package:collection
(fs.IoNode child) => child.name == name,
)
as fs.Directory?;
}
class _Progress {
@ -1821,7 +1889,9 @@ class _Progress {
Stopwatch? _lastUpdate;
void update({bool flush = false}) {
if (_lastUpdate == null || _lastUpdate!.elapsedMilliseconds >= millisecondsBetweenUpdates || flush) {
if (_lastUpdate == null ||
_lastUpdate!.elapsedMilliseconds >= millisecondsBetweenUpdates ||
flush) {
_lastUpdate ??= Stopwatch();
if (!quiet) {
final String line = toString();
@ -1842,13 +1912,17 @@ class _Progress {
String toString() {
final int percent = (100.0 * (_withLicense + _withoutLicense) / max).round();
return '${(_withLicense + _withoutLicense).toString().padLeft(10)} of ${max.toString().padRight(6)} '
'${'' * (percent ~/ 10)}${'' * (10 - (percent ~/ 10))} $percent% '
'${ _withoutLicense > 0 ? "($_withoutLicense missing licenses) " : ""}'
'$label';
'${'' * (percent ~/ 10)}${'' * (10 - (percent ~/ 10))} $percent% '
'${_withoutLicense > 0 ? "($_withoutLicense missing licenses) " : ""}'
'$label';
}
}
final RegExp _signaturePattern = RegExp(r'^Signature: (\w+)$', multiLine: true, expectNoMatch: true);
final RegExp _signaturePattern = RegExp(
r'^Signature: (\w+)$',
multiLine: true,
expectNoMatch: true,
);
/// Reads the signature from a golden file.
String? _readSignature(String goldenPath) {
@ -1879,11 +1953,17 @@ void _writeSignature(String signature, system.IOSink sink) {
// Checks for changes to the license tool itself.
//
// Returns true if changes are detected.
Future<bool> _computeLicenseToolChanges(_RepositoryDirectory root, { required String goldenSignaturePath, required String outputSignaturePath }) async {
Future<bool> _computeLicenseToolChanges(
_RepositoryDirectory root, {
required String goldenSignaturePath,
required String outputSignaturePath,
}) async {
final fs.Directory flutterNode = findChildDirectory(root.ioDirectory, 'flutter')!;
final fs.Directory toolsNode = findChildDirectory(flutterNode, 'tools')!;
final fs.Directory licenseNode = findChildDirectory(toolsNode, 'licenses')!;
final _RepositoryDirectory licenseToolDirectory = _RepositoryFlutterLicenseToolDirectory(licenseNode);
final _RepositoryDirectory licenseToolDirectory = _RepositoryFlutterLicenseToolDirectory(
licenseNode,
);
final String toolSignature = await licenseToolDirectory.signature;
final system.IOSink sink = system.File(outputSignaturePath).openWrite();
_writeSignature(toolSignature, sink);
@ -1896,7 +1976,8 @@ Future<bool> _computeLicenseToolChanges(_RepositoryDirectory root, { required St
///
/// If [writeSignature] is set, the signature is written to the output file.
/// If [force] is set, collection is run regardless of whether or not the signature matches.
Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
Future<void> _collectLicensesForComponent(
_RepositoryDirectory componentRoot, {
required String inputGoldenPath,
String? outputGoldenPath,
required bool writeSignature,
@ -1929,7 +2010,8 @@ Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
}
progress.label = 'Dumping results...';
progress.flush();
final List<String> output = licenses.map((GroupedLicense license) => license.toStringDebug()).toList();
final List<String> output =
licenses.map((GroupedLicense license) => license.toStringDebug()).toList();
for (int index = 0; index < output.length; index += 1) {
// The strings we look for here are strings which we do not expect to see in
// any of the licenses we use. They either represent examples of misparsing
@ -1943,7 +2025,9 @@ Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
if (output[index].contains('Version: MPL 1.1/GPL 2.0/LGPL 2.1')) {
throw 'Unexpected trilicense block found in:\n${output[index]}';
}
if (output[index].contains('The contents of this file are subject to the Mozilla Public License Version')) {
if (output[index].contains(
'The contents of this file are subject to the Mozilla Public License Version',
)) {
throw 'Unexpected MPL block found in:\n${output[index]}';
}
if (output[index].contains('You should have received a copy of the GNU')) {
@ -1952,16 +2036,22 @@ Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
if (output[index].contains('Contents of this folder are ported from')) {
throw 'Unexpected block found in:\n${output[index]}';
}
if (output[index].contains('https://github.com/w3c/web-platform-tests/tree/master/selectors-api')) {
if (output[index].contains(
'https://github.com/w3c/web-platform-tests/tree/master/selectors-api',
)) {
throw 'Unexpected W3C content found in:\n${output[index]}';
}
if (output[index].contains('http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html')) {
if (output[index].contains(
'http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html',
)) {
throw 'Unexpected W3C copyright found in:\n${output[index]}';
}
if (output[index].contains('It is based on commit')) {
throw 'Unexpected content found in:\n${output[index]}';
}
if (output[index].contains('The original code is covered by the dual-licensing approach described in:')) {
if (output[index].contains(
'The original code is covered by the dual-licensing approach described in:',
)) {
throw 'Unexpected old license reference found in:\n${output[index]}';
}
if (output[index].contains('must choose')) {
@ -1980,13 +2070,17 @@ Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
// MAIN
Future<void> main(List<String> arguments) async {
final ArgParser parser = ArgParser()
..addOption('src', help: 'The root of the engine source.')
..addOption('out', help: 'The directory where output is written. (Ignored if used with --release.)')
..addOption('golden', help: 'The directory containing golden results.')
..addFlag('quiet', help: 'If set, the diagnostic output is much less verbose.')
..addFlag('verbose', help: 'If set, print additional information to help with development.')
..addFlag('release', help: 'Print output in the format used for product releases.');
final ArgParser parser =
ArgParser()
..addOption('src', help: 'The root of the engine source.')
..addOption(
'out',
help: 'The directory where output is written. (Ignored if used with --release.)',
)
..addOption('golden', help: 'The directory containing golden results.')
..addFlag('quiet', help: 'If set, the diagnostic output is much less verbose.')
..addFlag('verbose', help: 'If set, print additional information to help with development.')
..addFlag('release', help: 'Print output in the format used for product releases.');
final ArgResults argResults = parser.parse(arguments);
final bool quiet = argResults['quiet'] as bool;
@ -1999,7 +2093,9 @@ Future<void> main(List<String> arguments) async {
}
if (!releaseMode) {
if (argResults['out'] == null || argResults['golden'] == null) {
print('Flutter license script: Must provide --out and --golden directories in non-release mode');
print(
'Flutter license script: Must provide --out and --golden directories in non-release mode',
);
print(parser.usage);
system.exit(1);
}
@ -2016,7 +2112,9 @@ Future<void> main(List<String> arguments) async {
try {
system.stderr.writeln('Finding files...');
final fs.FileSystemDirectory rootDirectory = fs.FileSystemDirectory.fromPath(argResults['src'] as String);
final fs.FileSystemDirectory rootDirectory = fs.FileSystemDirectory.fromPath(
argResults['src'] as String,
);
final _RepositoryDirectory root = _EngineSrcDirectory(rootDirectory);
if (releaseMode) {
@ -2029,9 +2127,9 @@ Future<void> main(List<String> arguments) async {
progress.label = 'Dumping results...';
progress.flush();
final String output = licenses
.where((GroupedLicense license) => license.body.isNotEmpty)
.map((GroupedLicense license) => license.toStringFormal())
.join('\n${"-" * 80}\n');
.where((GroupedLicense license) => license.body.isNotEmpty)
.map((GroupedLicense license) => license.toStringFormal())
.join('\n${"-" * 80}\n');
print(output);
progress.label = 'Done.';
progress.flush();
@ -2047,7 +2145,9 @@ Future<void> main(List<String> arguments) async {
outputSignaturePath: path.join(argResults['out'] as String, toolSignatureFilename),
);
if (forceRunAll) {
system.stderr.writeln('Detected changes to license tool. Forcing license collection for all components.');
system.stderr.writeln(
'Detected changes to license tool. Forcing license collection for all components.',
);
}
final List<String> usedGoldens = <String>[];
bool isFirstComponent = true;
@ -2061,9 +2161,9 @@ Future<void> main(List<String> arguments) async {
} else {
// For other components, we need a clean repository that does not
// contain any state left over from previous components.
componentRoot = _EngineSrcDirectory(rootDirectory)
.subdirectories
.firstWhere((_RepositoryDirectory dir) => dir.name == component.name);
componentRoot = _EngineSrcDirectory(
rootDirectory,
).subdirectories.firstWhere((_RepositoryDirectory dir) => dir.name == component.name);
}
final String goldenFileName = 'licenses_${component.io.name}';
await _collectLicensesForComponent(
@ -2080,23 +2180,30 @@ Future<void> main(List<String> arguments) async {
);
usedGoldens.add(goldenFileName);
}
final Set<String> unusedGoldens = system.Directory(argResults['golden'] as String).listSync()
.map<String>((system.FileSystemEntity file) => path.basename(file.path))
.where((String name) => name.startsWith('licenses_'))
.toSet()
..removeAll(usedGoldens);
final Set<String> unusedGoldens =
system.Directory(argResults['golden'] as String)
.listSync()
.map<String>((system.FileSystemEntity file) => path.basename(file.path))
.where((String name) => name.startsWith('licenses_'))
.toSet()
..removeAll(usedGoldens);
if (unusedGoldens.isNotEmpty) {
system.stderr.writeln('The following golden files in ${argResults['golden']} are unused and need to be deleted:');
system.stderr.writeln(
'The following golden files in ${argResults['golden']} are unused and need to be deleted:',
);
unusedGoldens.map((String s) => ' * $s').forEach(system.stderr.writeln);
system.exit(1);
}
// write to disk the list of files we did _not_ cover, so it's easier to catch in diffs
final String excluded = (_RepositoryDirectory._excluded.map(
(fs.IoNode node) => node.fullName,
).toSet().toList()..sort()).join('\n');
system.File(path.join(argResults['out'] as String, 'excluded_files')).writeAsStringSync(
'$excluded\n',
);
final String excluded = (_RepositoryDirectory._excluded
.map((fs.IoNode node) => node.fullName)
.toSet()
.toList()
..sort())
.join('\n');
system.File(
path.join(argResults['out'] as String, 'excluded_files'),
).writeAsStringSync('$excluded\n');
}
} catch (e, stack) {
system.stderr.writeln();

View File

@ -467,10 +467,16 @@ final List<Pattern> skippedFilePatterns = <Pattern>[
RegExp(r'/CHANGELOG(?:\.[.A-Z0-9]+)?$', caseSensitive: false),
RegExp(r'/INSTALL(?:\.[a-zA-Z0-9]+)?$'),
RegExp(r'/Makefile(?:\.[.A-Z0-9]+)?$', caseSensitive: false),
RegExp(r'\.~[0-9]+~$', expectNoMatch: true), // files that end in ".~1~", a backup convention of some IDEs
RegExp(
r'\.~[0-9]+~$',
expectNoMatch: true,
), // files that end in ".~1~", a backup convention of some IDEs
RegExp(r'\bmanual\.txt$'),
RegExp(r'^flutter/(?:.+/)*[^/]+_unittests?\.[^/]+$'),
RegExp(r'^flutter/lib/web_ui/lib/assets/ahem\.ttf$', expectNoMatch: true), // this gitignored file exists only for testing purposes
RegExp(
r'^flutter/lib/web_ui/lib/assets/ahem\.ttf$',
expectNoMatch: true,
), // this gitignored file exists only for testing purposes
RegExp(r'^flutter/sky/packages/sky_engine/LICENSE$'), // that is the output of this script
RegExp(r'^flutter/third_party/abseil-cpp/(?:.+/)*[^/]+_test\.[^/]+$'),
RegExp(r'^flutter/third_party/angle/(?:.+/)*[^/]+_unittest\.[^/]+$'),
@ -478,6 +484,8 @@ final List<Pattern> skippedFilePatterns = <Pattern>[
RegExp(r'^flutter/third_party/boringssl/src/crypto/fipsmodule/bn/[^/]+.go$'),
RegExp(r'^flutter/third_party/boringssl/src/crypto/fipsmodule/ec/[^/]+.go$'),
RegExp(r'^flutter/third_party/dart/(?:.+/)*[^/]+_test\.[^/]+$'),
RegExp(r'^flutter/third_party/freetype2/docs/(?!FTL\.TXT$).+'), // ignore all documentation except the license
RegExp(
r'^flutter/third_party/freetype2/docs/(?!FTL\.TXT$).+',
), // ignore all documentation except the license
RegExp(r'^flutter/third_party/zlib/(?:.+/)*[^/]+_unittest\.[^/]+$'),
];

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,16 @@ class RegExp implements core.RegExp {
bool unicode = false,
bool dotAll = false,
this.expectNoMatch = false,
}) : _pattern = core.RegExp(source, multiLine: multiLine, caseSensitive: caseSensitive, unicode: unicode, dotAll: dotAll),
source = _stripFrameNumber(StackTrace.current.toString().split('\n').skip(1).take(1).single) {
}) : _pattern = core.RegExp(
source,
multiLine: multiLine,
caseSensitive: caseSensitive,
unicode: unicode,
dotAll: dotAll,
),
source = _stripFrameNumber(
StackTrace.current.toString().split('\n').skip(1).take(1).single,
) {
_allPatterns.add(this);
}
@ -43,28 +51,36 @@ class RegExp implements core.RegExp {
stderr.writeln('Top ten patterns:');
patterns.sort((RegExp a, RegExp b) => b._stopwatch.elapsed.compareTo(a._stopwatch.elapsed));
for (final RegExp pattern in patterns.take(10)) {
stderr.writeln('${pattern._stopwatch.elapsedMicroseconds.toString().padLeft(10)}μs tests -- /${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})');
stderr.writeln(
'${pattern._stopwatch.elapsedMicroseconds.toString().padLeft(10)}μs tests -- /${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})',
);
}
stderr.writeln();
stderr.writeln('Unmatched patterns:');
patterns.sort((RegExp a, RegExp b) => a.pattern.compareTo(b.pattern));
for (final RegExp pattern in patterns) {
if (pattern.matchCount == 0 && !pattern.expectNoMatch && pattern.testCount > 0) {
stderr.writeln('/${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})');
stderr.writeln(
'/${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})',
);
}
}
stderr.writeln();
stderr.writeln('Unexpectedly matched patterns:');
for (final RegExp pattern in patterns) {
if (pattern.matchCount > 0 && pattern.expectNoMatch) {
stderr.writeln('/${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})');
stderr.writeln(
'/${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})',
);
}
}
stderr.writeln();
stderr.writeln('Unused patterns:');
for (final RegExp pattern in patterns) {
if (pattern.testCount == 0) {
stderr.writeln('/${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})');
stderr.writeln(
'/${pattern.pattern}/ (${pattern.testCount} tests, ${pattern.matchCount} matches, ${pattern.source})',
);
}
}
}

View File

@ -18,9 +18,15 @@ void main() {
});
test('Indenting blocks', () {
expect(reformat(' a\nb\n c'), 'a\nb\n c'); // strips leading indents
expect(reformat(' a\n b\n c'), 'a\nb\nc'); // strips common one-space indent, then strips stray one-space indents
expect(
reformat(' a\n b\n c'),
'a\nb\nc',
); // strips common one-space indent, then strips stray one-space indents
expect(reformat(' a\n b\n c'), 'a\nb\nc'); // strips common two-space indent
expect(reformat(' a\n b\n c'), 'a\nb\nc'); // strips common two-space indent, then strips stray one-space indent
expect(
reformat(' a\n b\n c'),
'a\nb\nc',
); // strips common two-space indent, then strips stray one-space indent
expect(reformat(' a\n b\n c'), 'a\n b\nc'); // streps common two-space indent
expect(reformat(' a\n b\n c'), 'a\n b\nc'); // streps common two-space indent
});
@ -46,42 +52,45 @@ void main() {
expect(reformat(' a\n a\n b\nc'), 'a\na\n b\nc');
});
test('Specific cases', () {
expect(reformat(' Apache\n Version\n Bla bla\n\nBla bla bla'), 'Apache\nVersion\nBla bla\n\nBla bla bla');
expect(
reformat(
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n'
'/* */\n'
'/* This software is made available under the terms of the */\n'
'/* ICU License -- ICU 1.8.1 and later. */\n'
),
'Copyright (c) IBM Corporation, 2000-2012. All rights reserved.\n'
'\n'
'This software is made available under the terms of the\n'
'ICU License -- ICU 1.8.1 and later.'
reformat(' Apache\n Version\n Bla bla\n\nBla bla bla'),
'Apache\nVersion\nBla bla\n\nBla bla bla',
);
expect(
reformat(
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n'
'/* */\n'
'/* This software is made available under the terms of the */\n'
'/* ICU License -- ICU 1.8.1 and later. */'
'/* ICU License -- ICU 1.8.1 and later. */\n',
),
'Copyright (c) IBM Corporation, 2000-2012. All rights reserved.\n'
'\n'
'This software is made available under the terms of the\n'
'ICU License -- ICU 1.8.1 and later.'
'ICU License -- ICU 1.8.1 and later.',
);
expect(
reformat(
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n'
'/* */\n'
'/* This software is made available under the terms of the */\n'
'/* ICU License -- ICU 1.8.1 and later.'
'/* ICU License -- ICU 1.8.1 and later. */',
),
'Copyright (c) IBM Corporation, 2000-2012. All rights reserved.\n'
'\n'
'This software is made available under the terms of the\n'
'ICU License -- ICU 1.8.1 and later.'
'ICU License -- ICU 1.8.1 and later.',
);
expect(
reformat(
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n'
'/* */\n'
'/* This software is made available under the terms of the */\n'
'/* ICU License -- ICU 1.8.1 and later.',
),
'Copyright (c) IBM Corporation, 2000-2012. All rights reserved.\n'
'\n'
'This software is made available under the terms of the\n'
'ICU License -- ICU 1.8.1 and later.',
);
});
}