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

View File

@ -38,7 +38,8 @@ bool _stripIndentation(List<String> lines) {
} else { } else {
prefix = leadingDecorations.matchAsPrefix(lines.first)?.group(0); 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; final int prefixLength = prefix.length;
for (int index = 0; index < lines.length; index += 1) { for (int index = 0; index < lines.length; index += 1) {
final String line = lines[index]; final String line = lines[index];
@ -168,7 +169,8 @@ String stripAsciiArt(String input) {
for (final List<String> image in asciiArtImages) { for (final List<String> image in asciiArtImages) {
assert(image.isNotEmpty); assert(image.isNotEmpty);
// Look for the image starting on each line. // 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]); final int x = lines[index].indexOf(image[0]);
if (x >= 0) { if (x >= 0) {
int width = image[0].length; int width = image[0].length;
@ -186,7 +188,8 @@ String stripAsciiArt(String input) {
final String text = lines[index + imageLine]; final String text = lines[index + imageLine];
assert(text.length > x); assert(text.length > x);
if (text.length >= x + width) { 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 { } else {
lines[index + imageLine] = text.substring(0, x); 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). // (e.g. having the copyright for one above the terms for the other and so on).
class _RepositoryOpaqueLicenseFile extends _RepositorySingleLicenseFile { class _RepositoryOpaqueLicenseFile extends _RepositorySingleLicenseFile {
_RepositoryOpaqueLicenseFile(_RepositoryDirectory parent, fs.TextFile io) _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 { class _RepositoryReadmeIjgFile extends _RepositorySingleLicenseFile {
@ -197,7 +206,8 @@ class _RepositoryReadmeIjgFile extends _RepositorySingleLicenseFile {
// The message we are required to include in our output. // The message we are required to include in our output.
// //
// We include it by just including the whole license. // 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. // 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'unaltered; and any additions, deletions, or changes to the original files\n'
r'must be clearly indicated in accompanying documentation\.\n' r'must be clearly indicated in accompanying documentation\.\n'
r'\(2\) If only executable code is distributed, then the accompanying\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'\(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'full responsibility for any undesirable consequences; the authors accept\n'
r'NO LIABILITY for damages of any kind\.\n', r'NO LIABILITY for damages of any kind\.\n',
@ -239,9 +251,7 @@ class _RepositoryDartLicenseFile extends _RepositorySingleLicenseFile {
_RepositoryDartLicenseFile(_RepositoryDirectory parent, fs.TextFile io) _RepositoryDartLicenseFile(_RepositoryDirectory parent, fs.TextFile io)
: super(parent, io, _parseLicense(io)); : super(parent, io, _parseLicense(io));
static final RegExp _pattern = RegExp( static final RegExp _pattern = RegExp(r'(Copyright (?:.|\n)+)$');
r'(Copyright (?:.|\n)+)$',
);
static License _parseLicense(fs.TextFile io) { static License _parseLicense(fs.TextFile io) {
final Match? match = _pattern.firstMatch(io.readString()); 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'- The zlib License, which is listed in \[simd/jsimdext\.inc\]\(simd/jsimdext\.inc\)\n'
r'\n' r'\n'
r' This license is a subset of the other two, and it covers the libjpeg-turbo\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) { static void _parseLicense(fs.TextFile io) {
@ -318,10 +328,13 @@ class _RepositoryLibJpegTurboLicenseFile extends _RepositoryLicenseFile {
@override @override
List<License> get licenses { List<License> get licenses {
if (_licenses == null) { if (_licenses == null) {
final _RepositoryLicenseFile readme = parent!.getChildByName('README.ijg') as _RepositoryReadmeIjgFile; final _RepositoryLicenseFile readme =
final _RepositorySourceFile main = parent!.getChildByName('turbojpeg.c') as _RepositorySourceFile; parent!.getChildByName('README.ijg') as _RepositoryReadmeIjgFile;
final _RepositorySourceFile main =
parent!.getChildByName('turbojpeg.c') as _RepositorySourceFile;
final _RepositoryDirectory simd = parent!.getChildByName('simd') as _RepositoryDirectory; 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 = <License>[];
_licenses!.addAll(readme.licenses); _licenses!.addAll(readme.licenses);
_licenses!.add(main.extractInternalLicense()); _licenses!.add(main.extractInternalLicense());
@ -332,8 +345,7 @@ class _RepositoryLibJpegTurboLicenseFile extends _RepositoryLicenseFile {
} }
class _RepositoryFreetypeLicenseFile extends _RepositoryLicenseFile { class _RepositoryFreetypeLicenseFile extends _RepositoryLicenseFile {
_RepositoryFreetypeLicenseFile(super.parent, super.io) _RepositoryFreetypeLicenseFile(super.parent, super.io) : _target = _parseLicense(io);
: _target = _parseLicense(io);
static final RegExp _pattern = RegExp( static final RegExp _pattern = RegExp(
r'FREETYPE LICENSES\n' 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'The MD5 checksum support \(only used for debugging in development\n'
r'builds\) is in the public domain\.\n' r'builds\) is in the public domain\.\n'
r'\n*' r'\n*'
r'--- end of LICENSE\.TXT ---\n*$' r'--- end of LICENSE\.TXT ---\n*$',
); );
static String _parseLicense(fs.TextFile io) { static String _parseLicense(fs.TextFile io) {
@ -426,7 +438,10 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
throw 'could not parse ICU license file'; throw 'could not parse ICU license file';
} }
const int groupCount = 22; 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; const int timeZoneGroup = 18;
if (match.group(timeZoneGroup)!.contains(copyrightMentionPattern)) { if (match.group(timeZoneGroup)!.contains(copyrightMentionPattern)) {
throw 'ICU: unexpected copyright in time zone database group\n:${match.group(timeZoneGroup)}'; 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 gplGroup1 = 20;
const int gplGroup2 = 21; 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'; 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._( return _RepositoryIcuLicenseFile._(
parent, parent,
io, io,
License.template(match.group(2)!, LicenseType.bsd, origin: io.fullName), License.template(match.group(2)!, LicenseType.bsd, origin: io.fullName),
License.fromMultipleBlocks( License.fromMultipleBlocks(
match.groups( match
Iterable<int> .groups(
.generate(groupCount, (int index) => index + 1) Iterable<int>.generate(
.where((int index) => !skippedGroups.contains(index)) groupCount,
.toList() (int index) => index + 1,
).cast<String>(), ).where((int index) => !skippedGroups.contains(index)).toList(),
)
.cast<String>(),
LicenseType.icu, LicenseType.icu,
origin: io.fullName, origin: io.fullName,
yesWeKnowWhatItLooksLikeButItIsNot: true, yesWeKnowWhatItLooksLikeButItIsNot: true,
@ -458,8 +476,12 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
); );
} }
_RepositoryIcuLicenseFile._(_RepositoryDirectory parent, fs.TextFile io, this.template, License license) _RepositoryIcuLicenseFile._(
: super(parent, io, license); _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+]. // Every paragraph of the license is mentioned. All newlines are disregarded [\n+].
static final RegExp _pattern = RegExp( static final RegExp _pattern = RegExp(
@ -563,29 +585,29 @@ class _RepositoryIcuLicenseFile extends _RepositorySingleLicenseFile {
); );
static const String gplExceptionExplanation1 = static const String gplExceptionExplanation1 =
'As a special exception to the GNU General Public License, if you\n' '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' 'distribute this file as part of a program that contains a\n'
'configuration script generated by Autoconf, you may include it under\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' 'the same distribution terms that you use for the rest of that\n'
'program.\n' 'program.\n'
'\n' '\n'
'\n' '\n'
'(The condition for the exception is fulfilled because\n' '(The condition for the exception is fulfilled because\n'
'ICU4C includes a configuration script generated by Autoconf,\n' 'ICU4C includes a configuration script generated by Autoconf,\n'
'namely the `configure` script.)'; 'namely the `configure` script.)';
static const String gplExceptionExplanation2 = static const String gplExceptionExplanation2 =
'As a special exception to the GNU General Public License, if you\n' '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' 'distribute this file as part of a program that contains a\n'
'configuration script generated by Autoconf, you may include it under\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' 'the same distribution terms that you use for the rest of that\n'
'program. This Exception is an additional permission under section 7\n' 'program. This Exception is an additional permission under section 7\n'
'of the GNU General Public License, version 3 ("GPLv3").\n' 'of the GNU General Public License, version 3 ("GPLv3").\n'
'\n' '\n'
'\n' '\n'
'(The condition for the exception is fulfilled because\n' '(The condition for the exception is fulfilled because\n'
'ICU4C includes a configuration script generated by Autoconf,\n' 'ICU4C includes a configuration script generated by Autoconf,\n'
'namely the `configure` script.)'; 'namely the `configure` script.)';
// Fixes an error in the license's formatting that our reformatter wouldn't be // 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 // 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 { class _RepositoryCxxStlDualLicenseFile extends _RepositoryLicenseFile {
_RepositoryCxxStlDualLicenseFile(super.parent, super.io) _RepositoryCxxStlDualLicenseFile(super.parent, super.io) : _licenses = _parseLicenses(io);
: _licenses = _parseLicenses(io);
static final RegExp _pattern = RegExp( static final RegExp _pattern = RegExp(
r'^' r'^'
@ -750,8 +771,7 @@ class _RepositoryCxxStlDualLicenseFile extends _RepositoryLicenseFile {
} }
class _RepositoryKhronosLicenseFile extends _RepositoryLicenseFile { class _RepositoryKhronosLicenseFile extends _RepositoryLicenseFile {
_RepositoryKhronosLicenseFile(super.parent, super.io) _RepositoryKhronosLicenseFile(super.parent, super.io) : _licenses = _parseLicenses(io);
: _licenses = _parseLicenses(io);
static final RegExp _pattern = RegExp( static final RegExp _pattern = RegExp(
r'^(Copyright .+?)\n' r'^(Copyright .+?)\n'
@ -852,16 +872,21 @@ class _RepositoryFuchsiaSdkLinuxLicenseFile extends _RepositorySingleLicenseFile
_RepositoryFuchsiaSdkLinuxLicenseFile(_RepositoryDirectory parent, fs.TextFile io) _RepositoryFuchsiaSdkLinuxLicenseFile(_RepositoryDirectory parent, fs.TextFile io)
: super(parent, io, _parseLicense(io)); : super(parent, io, _parseLicense(io));
static const String _pattern = 'The majority of files in this project use the Apache 2.0 License.\n' static const String _pattern =
'There are a few exceptions and their license can be found in the source.\n' 'The majority of files in this project use the Apache 2.0 License.\n'
'Any license deviations from Apache 2.0 are "more permissive" licenses.\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) { static License _parseLicense(fs.TextFile io) {
final String body = io.readString(); final String body = io.readString();
if (!body.startsWith(_pattern)) { if (!body.startsWith(_pattern)) {
throw 'unexpected Fuchsia license file contents'; 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)); : super(parent, io, _parseLicense(io));
static const String _prefix = static const String _prefix =
'The majority of files in this project use the Apache 2.0 License.\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' '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 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" "Any file without a license in it's source defaults to the repository Apache 2.0 License.\n"
'\n' '\n'
'===========================================================================================\n' '===========================================================================================\n'
'\n'; '\n';
static License _parseLicense(fs.TextFile io) { static License _parseLicense(fs.TextFile io) {
final String body = io.readString(); final String body = io.readString();
@ -892,7 +917,6 @@ class _RepositoryVulkanApacheLicenseFile extends _RepositorySingleLicenseFile {
} }
} }
// DIRECTORIES // DIRECTORIES
typedef _Constructor = _RepositoryFile Function(_RepositoryDirectory parent, fs.TextFile); typedef _Constructor = _RepositoryFile Function(_RepositoryDirectory parent, fs.TextFile);
@ -912,7 +936,7 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
List<_RepositoryDirectory> get subdirectories => _subdirectories; List<_RepositoryDirectory> get subdirectories => _subdirectories;
final Map<String,_RepositoryEntry> _childrenByName = <String,_RepositoryEntry>{}; final Map<String, _RepositoryEntry> _childrenByName = <String, _RepositoryEntry>{};
void crawl() { void crawl() {
for (final fs.IoNode entry in ioDirectory.walk) { 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". // 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>{ static const Map<String, _Constructor> _specialCaseFiles = <String, _Constructor>{
'/flutter/third_party/boringssl/src/LICENSE': _RepositoryOpenSSLLicenseFile.new, '/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/libpng/LICENSE': _RepositoryLibPngLicenseFile.new,
'/flutter/third_party/rapidjson/LICENSE': _RepositoryOpaqueLicenseFile.new, '/flutter/third_party/rapidjson/LICENSE': _RepositoryOpaqueLicenseFile.new,
'/flutter/third_party/rapidjson/license.txt': _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/linux/LICENSE.vulkan': _RepositoryFuchsiaSdkLinuxLicenseFile.new,
'/fuchsia/sdk/mac/LICENSE.vulkan': _RepositoryFuchsiaSdkLinuxLicenseFile.new, '/fuchsia/sdk/mac/LICENSE.vulkan': _RepositoryFuchsiaSdkLinuxLicenseFile.new,
'/third_party/khronos/LICENSE': _RepositoryKhronosLicenseFile.new, '/third_party/khronos/LICENSE': _RepositoryKhronosLicenseFile.new,
@ -1023,7 +1051,9 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
return _RepositoryBinaryFile(this, entry as fs.File); 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() { bool _canGoUp() {
assert(parent != null || isLicenseRoot); assert(parent != null || isLicenseRoot);
@ -1063,13 +1093,14 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
/// Searches the current directory for licenses of the specified type. /// Searches the current directory for licenses of the specified type.
License? _localLicenseWithType(LicenseType type) { License? _localLicenseWithType(LicenseType type) {
final List<License> licenses = _licenses.expand((_RepositoryLicenseFile license) { final List<License> licenses =
final License? result = license.licenseOfType(type); _licenses.expand((_RepositoryLicenseFile license) {
if (result != null) { final License? result = license.licenseOfType(type);
return <License>[result]; if (result != null) {
} return <License>[result];
return const <License>[]; }
}).toList(); return const <License>[];
}).toList();
if (licenses.length > 1) { if (licenses.length > 1) {
print('unexpectedly found multiple matching licenses in $name of type $type'); print('unexpectedly found multiple matching licenses in $name of type $type');
return null; return null;
@ -1083,8 +1114,11 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
/// Searches all subdirectories (depth-first) for a license of the specified type. /// Searches all subdirectories (depth-first) for a license of the specified type.
License? _fullWalkDownForLicenseWithType(LicenseType type) { License? _fullWalkDownForLicenseWithType(LicenseType type) {
for (final _RepositoryDirectory directory in _subdirectories) { for (final _RepositoryDirectory directory in _subdirectories) {
if (directory._canGoUp()) { // avoid crawling into other license scopes if (directory._canGoUp()) {
final License? result = directory._localLicenseWithType(type) ?? directory._fullWalkDownForLicenseWithType(type); // avoid crawling into other license scopes
final License? result =
directory._localLicenseWithType(type) ??
directory._fullWalkDownForLicenseWithType(type);
if (result != null) { if (result != null) {
return result; return result;
} }
@ -1108,13 +1142,13 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
if (_canGoUp()) { if (_canGoUp()) {
return parent!.nearestLicenseWithName(name, authors: authors); return parent!.nearestLicenseWithName(name, authors: authors);
} }
return _fullWalkDownForLicenseWithName(name, authors: authors) return _fullWalkDownForLicenseWithName(name, authors: authors) ??
?? (authors != null ? parent?._fullWalkUpForLicenseWithName(name, authors: authors) : null); (authors != null ? parent?._fullWalkUpForLicenseWithName(name, authors: authors) : null);
}); });
return result; return result;
} }
License? _localLicenseWithName(String name, { String? authors }) { License? _localLicenseWithName(String name, {String? authors}) {
final _RepositoryEntry? entry = _childrenByName[name]; final _RepositoryEntry? entry = _childrenByName[name];
License? license; License? license;
if (entry is _RepositoryLicensedFile) { if (entry is _RepositoryLicensedFile) {
@ -1132,7 +1166,7 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
return license; 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 // 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 // to the top of the local license root, then from there check all the
// ancestors and all the descendants. // 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 // Authors license is at the root, and is sometimes mentioned in various
// files deep inside third party directories). // files deep inside third party directories).
return _localLicenseWithName(name, authors: authors) ?? 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) { for (final _RepositoryDirectory directory in _subdirectories) {
if (directory._canGoUp()) { // avoid crawling into other license scopes if (directory._canGoUp()) {
final License? result = directory._localLicenseWithName(name, authors: authors) // avoid crawling into other license scopes
?? directory._fullWalkDownForLicenseWithName(name, authors: authors); final License? result =
directory._localLicenseWithName(name, authors: authors) ??
directory._fullWalkDownForLicenseWithName(name, authors: authors);
if (result != null) { if (result != null) {
return result; return result;
} }
@ -1246,8 +1282,10 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource {
/// this directory tree. /// this directory tree.
Future<String> get signature async { Future<String> get signature async {
final List<_RepositoryLicensedFile> allFiles = _signatureFiles.toList(); final List<_RepositoryLicensedFile> allFiles = _signatureFiles.toList();
allFiles.sort((_RepositoryLicensedFile a, _RepositoryLicensedFile b) => allFiles.sort(
a.io.fullName.compareTo(b.io.fullName)); (_RepositoryLicensedFile a, _RepositoryLicensedFile b) =>
a.io.fullName.compareTo(b.io.fullName),
);
final crypto.Digest digest = await crypto.md5.bind(_signatureStream(allFiles)).single; final crypto.Digest digest = await crypto.md5.bind(_signatureStream(allFiles)).single;
return digest.bytes.map((int e) => e.toRadixString(16).padLeft(2, '0')).join(); return digest.bytes.map((int e) => e.toRadixString(16).padLeft(2, '0')).join();
} }
@ -1277,12 +1315,19 @@ class _RepositoryReachOutFile extends _RepositoryLicensedFile {
directory = directory.parent; directory = directory.parent;
index -= 1; 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 { 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 Set<String> reachOutFilenames;
final int offset; final int offset;
@ -1356,13 +1401,25 @@ class _RepositoryInjaJsonFile extends _RepositorySourceFile {
throw '${io.fullName} has changed contents.'; throw '${io.fullName} has changed contents.';
} }
final String license = match.group(3)!; final String license = match.group(3)!;
_internalLicenses = match.groups(const <int>[ 2, 6, 8 ]).map<License>((String? copyright) { _internalLicenses =
assert(copyright!.contains('Copyright')); match.groups(const <int>[2, 6, 8]).map<License>((String? copyright) {
return License.fromCopyrightAndLicense(copyright!, license, LicenseType.mit, origin: io.fullName); assert(copyright!.contains('Copyright'));
}).toList(); return License.fromCopyrightAndLicense(
assert(!match.groups(const <int>[ 1, 4, 5, 7, 9, 10, 11]).any((String? text) => text!.contains(copyrightMentionPattern))); 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 @override
bool shouldRecurse(fs.IoNode entry) { bool shouldRecurse(fs.IoNode entry) {
return entry.name != 'third_party' // all third_party components have been moved to flutter/third_party return entry.name !=
&& super.shouldRecurse(entry); 'third_party' // all third_party components have been moved to flutter/third_party
&&
super.shouldRecurse(entry);
} }
@override @override
@ -1520,7 +1579,10 @@ class _RepositoryFallbackRootCertificatesDirectory extends _RepositoryDirectory
@override @override
String get officialSourceLocation { 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) { 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}'; 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. // exact same license text as in LICENSE.
@override @override
License? nearestLicenseWithName(String name, { String? authors }) { License? nearestLicenseWithName(String name, {String? authors}) {
if (name == 'MiniZip_info.txt') { if (name == 'MiniZip_info.txt') {
return super.nearestLicenseWithName('LICENSE', authors: authors)!; return super.nearestLicenseWithName('LICENSE', authors: authors)!;
} }
@ -1663,7 +1725,9 @@ class _RepositoryBoringSSLDirectory extends _RepositoryDirectory {
@override @override
License? nearestLicenseWithName(String name, {String? authors}) { License? nearestLicenseWithName(String name, {String? authors}) {
assert(!src._canGoUp()); 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; return result;
} }
@ -1713,9 +1777,13 @@ class _RepositoryFlutterThirdPartyDirectory extends _RepositoryGenericThirdParty
@override @override
bool shouldRecurse(fs.IoNode entry) { bool shouldRecurse(fs.IoNode entry) {
return entry.name != 'skia' // handled as a virtual directory of the root return entry.name !=
&& entry.name != 'dart' // handled as a virtual directory of the root 'skia' // handled as a virtual directory of the root
&& super.shouldRecurse(entry); &&
entry.name !=
'dart' // handled as a virtual directory of the root
&&
super.shouldRecurse(entry);
} }
@override @override
@ -1756,7 +1824,6 @@ class _RepositoryGpuShimDirectory extends _RepositoryDirectory {
String get libraryName => 'engine'; String get libraryName => 'engine';
} }
/// The license tool directory. /// The license tool directory.
/// ///
/// This is a special-case root node that is not used for license aggregation, /// This is a special-case root node that is not used for license aggregation,
@ -1772,13 +1839,14 @@ class _RepositoryFlutterLicenseToolDirectory extends _RepositoryDirectory {
} }
} }
// BOOTSTRAPPING LOGIC // BOOTSTRAPPING LOGIC
fs.Directory? findChildDirectory(fs.Directory parent, String name) { fs.Directory? findChildDirectory(fs.Directory parent, String name) {
return parent.walk.firstWhereOrNull( // from IterableExtension in package:collection return parent.walk.firstWhereOrNull(
(fs.IoNode child) => child.name == name, // from IterableExtension in package:collection
) as fs.Directory?; (fs.IoNode child) => child.name == name,
)
as fs.Directory?;
} }
class _Progress { class _Progress {
@ -1821,7 +1889,9 @@ class _Progress {
Stopwatch? _lastUpdate; Stopwatch? _lastUpdate;
void update({bool flush = false}) { void update({bool flush = false}) {
if (_lastUpdate == null || _lastUpdate!.elapsedMilliseconds >= millisecondsBetweenUpdates || flush) { if (_lastUpdate == null ||
_lastUpdate!.elapsedMilliseconds >= millisecondsBetweenUpdates ||
flush) {
_lastUpdate ??= Stopwatch(); _lastUpdate ??= Stopwatch();
if (!quiet) { if (!quiet) {
final String line = toString(); final String line = toString();
@ -1842,13 +1912,17 @@ class _Progress {
String toString() { String toString() {
final int percent = (100.0 * (_withLicense + _withoutLicense) / max).round(); final int percent = (100.0 * (_withLicense + _withoutLicense) / max).round();
return '${(_withLicense + _withoutLicense).toString().padLeft(10)} of ${max.toString().padRight(6)} ' return '${(_withLicense + _withoutLicense).toString().padLeft(10)} of ${max.toString().padRight(6)} '
'${'' * (percent ~/ 10)}${'' * (10 - (percent ~/ 10))} $percent% ' '${'' * (percent ~/ 10)}${'' * (10 - (percent ~/ 10))} $percent% '
'${ _withoutLicense > 0 ? "($_withoutLicense missing licenses) " : ""}' '${_withoutLicense > 0 ? "($_withoutLicense missing licenses) " : ""}'
'$label'; '$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. /// Reads the signature from a golden file.
String? _readSignature(String goldenPath) { String? _readSignature(String goldenPath) {
@ -1879,11 +1953,17 @@ void _writeSignature(String signature, system.IOSink sink) {
// Checks for changes to the license tool itself. // Checks for changes to the license tool itself.
// //
// Returns true if changes are detected. // 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 flutterNode = findChildDirectory(root.ioDirectory, 'flutter')!;
final fs.Directory toolsNode = findChildDirectory(flutterNode, 'tools')!; final fs.Directory toolsNode = findChildDirectory(flutterNode, 'tools')!;
final fs.Directory licenseNode = findChildDirectory(toolsNode, 'licenses')!; final fs.Directory licenseNode = findChildDirectory(toolsNode, 'licenses')!;
final _RepositoryDirectory licenseToolDirectory = _RepositoryFlutterLicenseToolDirectory(licenseNode); final _RepositoryDirectory licenseToolDirectory = _RepositoryFlutterLicenseToolDirectory(
licenseNode,
);
final String toolSignature = await licenseToolDirectory.signature; final String toolSignature = await licenseToolDirectory.signature;
final system.IOSink sink = system.File(outputSignaturePath).openWrite(); final system.IOSink sink = system.File(outputSignaturePath).openWrite();
_writeSignature(toolSignature, sink); _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 [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. /// 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, required String inputGoldenPath,
String? outputGoldenPath, String? outputGoldenPath,
required bool writeSignature, required bool writeSignature,
@ -1929,7 +2010,8 @@ Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
} }
progress.label = 'Dumping results...'; progress.label = 'Dumping results...';
progress.flush(); 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) { 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 // 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 // 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')) { if (output[index].contains('Version: MPL 1.1/GPL 2.0/LGPL 2.1')) {
throw 'Unexpected trilicense block found in:\n${output[index]}'; 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]}'; throw 'Unexpected MPL block found in:\n${output[index]}';
} }
if (output[index].contains('You should have received a copy of the GNU')) { 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')) { if (output[index].contains('Contents of this folder are ported from')) {
throw 'Unexpected block found in:\n${output[index]}'; 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]}'; 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]}'; throw 'Unexpected W3C copyright found in:\n${output[index]}';
} }
if (output[index].contains('It is based on commit')) { if (output[index].contains('It is based on commit')) {
throw 'Unexpected content found in:\n${output[index]}'; 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]}'; throw 'Unexpected old license reference found in:\n${output[index]}';
} }
if (output[index].contains('must choose')) { if (output[index].contains('must choose')) {
@ -1980,13 +2070,17 @@ Future<void> _collectLicensesForComponent(_RepositoryDirectory componentRoot, {
// MAIN // MAIN
Future<void> main(List<String> arguments) async { Future<void> main(List<String> arguments) async {
final ArgParser parser = ArgParser() final ArgParser parser =
..addOption('src', help: 'The root of the engine source.') ArgParser()
..addOption('out', help: 'The directory where output is written. (Ignored if used with --release.)') ..addOption('src', help: 'The root of the engine source.')
..addOption('golden', help: 'The directory containing golden results.') ..addOption(
..addFlag('quiet', help: 'If set, the diagnostic output is much less verbose.') 'out',
..addFlag('verbose', help: 'If set, print additional information to help with development.') help: 'The directory where output is written. (Ignored if used with --release.)',
..addFlag('release', help: 'Print output in the format used for product releases.'); )
..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 ArgResults argResults = parser.parse(arguments);
final bool quiet = argResults['quiet'] as bool; final bool quiet = argResults['quiet'] as bool;
@ -1999,7 +2093,9 @@ Future<void> main(List<String> arguments) async {
} }
if (!releaseMode) { if (!releaseMode) {
if (argResults['out'] == null || argResults['golden'] == null) { 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); print(parser.usage);
system.exit(1); system.exit(1);
} }
@ -2016,7 +2112,9 @@ Future<void> main(List<String> arguments) async {
try { try {
system.stderr.writeln('Finding files...'); 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); final _RepositoryDirectory root = _EngineSrcDirectory(rootDirectory);
if (releaseMode) { if (releaseMode) {
@ -2029,9 +2127,9 @@ Future<void> main(List<String> arguments) async {
progress.label = 'Dumping results...'; progress.label = 'Dumping results...';
progress.flush(); progress.flush();
final String output = licenses final String output = licenses
.where((GroupedLicense license) => license.body.isNotEmpty) .where((GroupedLicense license) => license.body.isNotEmpty)
.map((GroupedLicense license) => license.toStringFormal()) .map((GroupedLicense license) => license.toStringFormal())
.join('\n${"-" * 80}\n'); .join('\n${"-" * 80}\n');
print(output); print(output);
progress.label = 'Done.'; progress.label = 'Done.';
progress.flush(); progress.flush();
@ -2047,7 +2145,9 @@ Future<void> main(List<String> arguments) async {
outputSignaturePath: path.join(argResults['out'] as String, toolSignatureFilename), outputSignaturePath: path.join(argResults['out'] as String, toolSignatureFilename),
); );
if (forceRunAll) { 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>[]; final List<String> usedGoldens = <String>[];
bool isFirstComponent = true; bool isFirstComponent = true;
@ -2061,9 +2161,9 @@ Future<void> main(List<String> arguments) async {
} else { } else {
// For other components, we need a clean repository that does not // For other components, we need a clean repository that does not
// contain any state left over from previous components. // contain any state left over from previous components.
componentRoot = _EngineSrcDirectory(rootDirectory) componentRoot = _EngineSrcDirectory(
.subdirectories rootDirectory,
.firstWhere((_RepositoryDirectory dir) => dir.name == component.name); ).subdirectories.firstWhere((_RepositoryDirectory dir) => dir.name == component.name);
} }
final String goldenFileName = 'licenses_${component.io.name}'; final String goldenFileName = 'licenses_${component.io.name}';
await _collectLicensesForComponent( await _collectLicensesForComponent(
@ -2080,23 +2180,30 @@ Future<void> main(List<String> arguments) async {
); );
usedGoldens.add(goldenFileName); usedGoldens.add(goldenFileName);
} }
final Set<String> unusedGoldens = system.Directory(argResults['golden'] as String).listSync() final Set<String> unusedGoldens =
.map<String>((system.FileSystemEntity file) => path.basename(file.path)) system.Directory(argResults['golden'] as String)
.where((String name) => name.startsWith('licenses_')) .listSync()
.toSet() .map<String>((system.FileSystemEntity file) => path.basename(file.path))
..removeAll(usedGoldens); .where((String name) => name.startsWith('licenses_'))
.toSet()
..removeAll(usedGoldens);
if (unusedGoldens.isNotEmpty) { 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); unusedGoldens.map((String s) => ' * $s').forEach(system.stderr.writeln);
system.exit(1); system.exit(1);
} }
// write to disk the list of files we did _not_ cover, so it's easier to catch in diffs // 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( final String excluded = (_RepositoryDirectory._excluded
(fs.IoNode node) => node.fullName, .map((fs.IoNode node) => node.fullName)
).toSet().toList()..sort()).join('\n'); .toSet()
system.File(path.join(argResults['out'] as String, 'excluded_files')).writeAsStringSync( .toList()
'$excluded\n', ..sort())
); .join('\n');
system.File(
path.join(argResults['out'] as String, 'excluded_files'),
).writeAsStringSync('$excluded\n');
} }
} catch (e, stack) { } catch (e, stack) {
system.stderr.writeln(); system.stderr.writeln();

View File

@ -467,10 +467,16 @@ final List<Pattern> skippedFilePatterns = <Pattern>[
RegExp(r'/CHANGELOG(?:\.[.A-Z0-9]+)?$', caseSensitive: false), RegExp(r'/CHANGELOG(?:\.[.A-Z0-9]+)?$', caseSensitive: false),
RegExp(r'/INSTALL(?:\.[a-zA-Z0-9]+)?$'), RegExp(r'/INSTALL(?:\.[a-zA-Z0-9]+)?$'),
RegExp(r'/Makefile(?:\.[.A-Z0-9]+)?$', caseSensitive: false), 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'\bmanual\.txt$'),
RegExp(r'^flutter/(?:.+/)*[^/]+_unittests?\.[^/]+$'), 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/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/abseil-cpp/(?:.+/)*[^/]+_test\.[^/]+$'),
RegExp(r'^flutter/third_party/angle/(?:.+/)*[^/]+_unittest\.[^/]+$'), 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/bn/[^/]+.go$'),
RegExp(r'^flutter/third_party/boringssl/src/crypto/fipsmodule/ec/[^/]+.go$'), RegExp(r'^flutter/third_party/boringssl/src/crypto/fipsmodule/ec/[^/]+.go$'),
RegExp(r'^flutter/third_party/dart/(?:.+/)*[^/]+_test\.[^/]+$'), 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\.[^/]+$'), 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 unicode = false,
bool dotAll = false, bool dotAll = false,
this.expectNoMatch = false, this.expectNoMatch = false,
}) : _pattern = core.RegExp(source, multiLine: multiLine, caseSensitive: caseSensitive, unicode: unicode, dotAll: dotAll), }) : _pattern = core.RegExp(
source = _stripFrameNumber(StackTrace.current.toString().split('\n').skip(1).take(1).single) { source,
multiLine: multiLine,
caseSensitive: caseSensitive,
unicode: unicode,
dotAll: dotAll,
),
source = _stripFrameNumber(
StackTrace.current.toString().split('\n').skip(1).take(1).single,
) {
_allPatterns.add(this); _allPatterns.add(this);
} }
@ -43,28 +51,36 @@ class RegExp implements core.RegExp {
stderr.writeln('Top ten patterns:'); stderr.writeln('Top ten patterns:');
patterns.sort((RegExp a, RegExp b) => b._stopwatch.elapsed.compareTo(a._stopwatch.elapsed)); patterns.sort((RegExp a, RegExp b) => b._stopwatch.elapsed.compareTo(a._stopwatch.elapsed));
for (final RegExp pattern in patterns.take(10)) { 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();
stderr.writeln('Unmatched patterns:'); stderr.writeln('Unmatched patterns:');
patterns.sort((RegExp a, RegExp b) => a.pattern.compareTo(b.pattern)); patterns.sort((RegExp a, RegExp b) => a.pattern.compareTo(b.pattern));
for (final RegExp pattern in patterns) { for (final RegExp pattern in patterns) {
if (pattern.matchCount == 0 && !pattern.expectNoMatch && pattern.testCount > 0) { 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();
stderr.writeln('Unexpectedly matched patterns:'); stderr.writeln('Unexpectedly matched patterns:');
for (final RegExp pattern in patterns) { for (final RegExp pattern in patterns) {
if (pattern.matchCount > 0 && pattern.expectNoMatch) { 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();
stderr.writeln('Unused patterns:'); stderr.writeln('Unused patterns:');
for (final RegExp pattern in patterns) { for (final RegExp pattern in patterns) {
if (pattern.testCount == 0) { 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', () { test('Indenting blocks', () {
expect(reformat(' a\nb\n c'), 'a\nb\n c'); // strips leading indents 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
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
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'); expect(reformat(' a\n a\n b\nc'), 'a\na\n b\nc');
}); });
test('Specific cases', () { test('Specific cases', () {
expect(reformat(' Apache\n Version\n Bla bla\n\nBla bla bla'), 'Apache\nVersion\nBla bla\n\nBla bla bla');
expect( expect(
reformat( reformat(' Apache\n Version\n Bla bla\n\nBla bla bla'),
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n' 'Apache\nVersion\nBla bla\n\nBla bla bla',
'/* */\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.'
); );
expect( expect(
reformat( reformat(
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n' '/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n'
'/* */\n' '/* */\n'
'/* This software is made available under the terms of the */\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' 'Copyright (c) IBM Corporation, 2000-2012. All rights reserved.\n'
'\n' '\n'
'This software is made available under the terms of the\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( expect(
reformat( reformat(
'/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n' '/* Copyright (c) IBM Corporation, 2000-2012. All rights reserved. */\n'
'/* */\n' '/* */\n'
'/* This software is made available under the terms of the */\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' 'Copyright (c) IBM Corporation, 2000-2012. All rights reserved.\n'
'\n' '\n'
'This software is made available under the terms of the\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.',
); );
}); });
} }