mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
161 lines
5.7 KiB
Dart
161 lines
5.7 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:package_config/package_config.dart';
|
|
|
|
import 'base/file_system.dart';
|
|
|
|
/// Processes dependencies into a string representing the NOTICES file.
|
|
///
|
|
/// Reads the NOTICES or LICENSE file from each package in the .packages file,
|
|
/// splitting each one into each component license so that it can be de-duped
|
|
/// if possible. If the NOTICES file exists, it is preferred over the LICENSE
|
|
/// file.
|
|
///
|
|
/// Individual licenses inside each LICENSE file should be separated by 80
|
|
/// hyphens on their own on a line.
|
|
///
|
|
/// If a LICENSE or NOTICES file contains more than one component license,
|
|
/// then each component license must start with the names of the packages to
|
|
/// which the component license applies, with each package name on its own line
|
|
/// and the list of package names separated from the actual license text by a
|
|
/// blank line. The packages need not match the names of the pub package. For
|
|
/// example, a package might itself contain code from multiple third-party
|
|
/// sources, and might need to include a license for each one.
|
|
class LicenseCollector {
|
|
LicenseCollector({
|
|
required FileSystem fileSystem
|
|
}) : _fileSystem = fileSystem;
|
|
|
|
final FileSystem _fileSystem;
|
|
|
|
/// The expected separator for multiple licenses.
|
|
static final String licenseSeparator = '\n' + ('-' * 80) + '\n';
|
|
|
|
/// Obtain licenses from the `packageMap` into a single result.
|
|
///
|
|
/// [additionalLicenses] should contain aggregated license files from all
|
|
/// of the current applications dependencies.
|
|
LicenseResult obtainLicenses(
|
|
PackageConfig packageConfig,
|
|
Map<String, List<File>> additionalLicenses,
|
|
) {
|
|
final Map<String, Set<String>> packageLicenses = <String, Set<String>>{};
|
|
final Set<String> allPackages = <String>{};
|
|
final List<File> dependencies = <File>[];
|
|
|
|
for (final Package package in packageConfig.packages) {
|
|
final Uri packageUri = package.packageUriRoot;
|
|
if (packageUri == null || packageUri.scheme != 'file') {
|
|
continue;
|
|
}
|
|
// First check for NOTICES, then fallback to LICENSE
|
|
File file = _fileSystem.file(packageUri.resolve('../NOTICES'));
|
|
if (!file.existsSync()) {
|
|
file = _fileSystem.file(packageUri.resolve('../LICENSE'));
|
|
}
|
|
if (!file.existsSync()) {
|
|
continue;
|
|
}
|
|
|
|
dependencies.add(file);
|
|
final List<String> rawLicenses = file
|
|
.readAsStringSync()
|
|
.split(licenseSeparator);
|
|
for (final String rawLicense in rawLicenses) {
|
|
List<String> packageNames = <String>[];
|
|
String? licenseText;
|
|
if (rawLicenses.length > 1) {
|
|
final int split = rawLicense.indexOf('\n\n');
|
|
if (split >= 0) {
|
|
packageNames = rawLicense.substring(0, split).split('\n');
|
|
licenseText = rawLicense.substring(split + 2);
|
|
}
|
|
}
|
|
if (licenseText == null) {
|
|
packageNames = <String>[package.name];
|
|
licenseText = rawLicense;
|
|
}
|
|
packageLicenses.putIfAbsent(licenseText, () => <String>{}).addAll(packageNames);
|
|
allPackages.addAll(packageNames);
|
|
}
|
|
}
|
|
|
|
final List<String> combinedLicensesList = packageLicenses.keys
|
|
.map<String>((String license) {
|
|
final List<String> packageNames = packageLicenses[license]!.toList()
|
|
..sort();
|
|
return packageNames.join('\n') + '\n\n' + license;
|
|
}).toList();
|
|
combinedLicensesList.sort();
|
|
|
|
/// Append additional LICENSE files as specified in the pubspec.yaml.
|
|
final List<String> additionalLicenseText = <String>[];
|
|
final List<String> errorMessages = <String>[];
|
|
for (final String package in additionalLicenses.keys) {
|
|
for (final File license in additionalLicenses[package]!) {
|
|
if (!license.existsSync()) {
|
|
errorMessages.add(
|
|
'package $package specified an additional license at ${license.path}, but this file '
|
|
'does not exist.'
|
|
);
|
|
continue;
|
|
}
|
|
dependencies.add(license);
|
|
try {
|
|
additionalLicenseText.add(license.readAsStringSync());
|
|
} on FormatException catch (err) {
|
|
// File has an invalid encoding.
|
|
errorMessages.add(
|
|
'package $package specified an additional license at ${license.path}, but this file '
|
|
'could not be read:\n$err'
|
|
);
|
|
} on FileSystemException catch (err) {
|
|
// File cannot be parsed.
|
|
errorMessages.add(
|
|
'package $package specified an additional license at ${license.path}, but this file '
|
|
'could not be read:\n$err'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (errorMessages.isNotEmpty) {
|
|
return LicenseResult(
|
|
combinedLicenses: '',
|
|
dependencies: <File>[],
|
|
errorMessages: errorMessages,
|
|
);
|
|
}
|
|
|
|
final String combinedLicenses = combinedLicensesList
|
|
.followedBy(additionalLicenseText)
|
|
.join(licenseSeparator);
|
|
|
|
return LicenseResult(
|
|
combinedLicenses: combinedLicenses,
|
|
dependencies: dependencies,
|
|
errorMessages: errorMessages,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// The result of processing licenses with a [LicenseCollector].
|
|
class LicenseResult {
|
|
const LicenseResult({
|
|
required this.combinedLicenses,
|
|
required this.dependencies,
|
|
required this.errorMessages,
|
|
});
|
|
|
|
/// The raw text of the consumed licenses.
|
|
final String combinedLicenses;
|
|
|
|
/// Each license file that was consumed as input.
|
|
final List<File> dependencies;
|
|
|
|
/// If non-empty, license collection failed and this messages should
|
|
/// be displayed by the asset parser.
|
|
final List<String> errorMessages;
|
|
}
|