blob: 136cb8c8d57f203f098a327790cfc64d4fb998ec [file] [log] [blame]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'data_files.dart';
// Shared tools used by other libraries.
/// Quick and dirty caching URI loader.
///
/// Reads from [targetFile] if it exists and [forceLoad] is not `true`.
/// Otherwise fetches from [location] URI and stores in [targetFile].
///
/// If [targetFile] is omitted, a file in the system temporary directory
/// is used instead.
Future<String> fetch(
String location, {
File? targetFile,
bool forceLoad = false,
}) async {
if (targetFile == null) {
var safeLocation = safePath(location);
targetFile = File(path(Directory.systemTemp.path, safeLocation));
}
if (!forceLoad && targetFile.existsSync()) {
return targetFile.readAsString();
}
var uri = Uri.parse(location);
String contents;
if (uri.isScheme('file')) {
contents = File.fromUri(uri).readAsStringSync();
} else {
var client = HttpClient();
var request = await client.getUrl(uri);
var response = await request.close();
if (response.statusCode != HttpStatus.ok) {
throw HttpException(response.reasonPhrase, uri: uri);
}
contents = await utf8.decoder.bind(response).join();
client.close();
}
writeToPath(targetFile, contents);
return contents;
}
/// Writes string to file.
///
/// Ensures directory of file exits.
void writeToPath(File targetFile, String contents) {
var parentDir = Directory(parentPath(targetFile.path));
parentDir.createSync(recursive: true);
targetFile.writeAsStringSync(contents);
}
// Parent directory path of file or directory path.
String parentPath(String path) {
var end = path.length;
if (path.endsWith('/')) end -= 1;
var lastSlash = path.lastIndexOf('/', end);
if (lastSlash >= 0) {
return path.substring(0, lastSlash + 1);
}
if (path == '/') return path;
return './'; // Empty relative path.
}
Future<bool> checkLicense(bool acceptLicenseChange) async {
if (await licenseFile.checkChange() case var changedLicensePath?) {
if (!acceptLicenseChange) {
stderr.writeln(
licenseChangeWarning(licenseFile.targetLocation, changedLicensePath),
);
return false;
}
stderr.writeln('LICENSE CHANGE ACCEPTED!');
licenseFile.copyFrom(changedLicensePath);
} else if (acceptLicenseChange) {
stderr.writeln('Accepting license change with no change.');
stderr.writeln('DO NOT AUTOMATE LICENSE ACCEPTANCE!');
return false;
}
return true;
}
/// Warning shown if the license has changed.
String licenseChangeWarning(String originalPath, String newPath) => '''
**NOTICE**
The license file has changed. Check that it has not changed meaning.
See changes using:
git diff ${_windowize(originalPath)} ${_windowize(newPath)}
''';
const copyright = '''
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
''';
// Generated file header:
void writeHeader(StringSink output, List<DataFile> dependencies) {
output
..write(copyright)
..writeln('// Generated code. Do not edit.')
..writeln('// Generated from:');
for (var sourceFile in dependencies) {
output
..write('// - [')
..write(sourceFile.sourceLocation)
..write('](../../')
..write(sourceFile.targetLocation)
..writeln(')');
}
output
..writeln('// Licensed under the Unicode Inc. License Agreement')
..writeln(
'// (${licenseFile.sourceLocation}, '
'../../third_party/${licenseFile.targetLocation})',
);
}
/// Temporary directory. Created once and for all.
Directory get tmpDirectory => _tmpDirectory ??=
Directory.systemTemp.createTempSync('dart_pkg_characters');
Directory? _tmpDirectory;
/// Combines file paths into one path.
///
/// No fancy stuff, just adds path separator between parts,
/// if previous part doesn't end with one.
/// (Don't let later parts start with a path separator!)
/// Converts forward slashes to backwards slashes in Windows.
///
/// Empty parts are ignored.
String path(String path, [String path2 = '', String path3 = '']) {
var separator = Platform.pathSeparator;
path = _windowize(path);
if (path2.isEmpty && path3.isEmpty) return path;
var buffer = StringBuffer(path);
var prev = path;
for (var part in [path2, path3]) {
if (part.isEmpty) continue;
part = _windowize(part);
if (!prev.endsWith(separator)) {
buffer.write(separator);
}
buffer.write(part);
prev = part;
}
return buffer.toString();
}
/// Converts path to Windows path if on Windows (`/` to `\`).
///
/// Returns original path if not on Windows.
String _windowize(String path) =>
Platform.isWindows ? path.replaceAll('/', r'') : path;
/// Package root directory.
String packageRoot = _findRootDir().path;
/// A path relative to the [packageRoot].
String packagePath(String path2, [String path3 = '']) =>
path(packageRoot, path2, path3);
/// A path relative to a temporary directory.
String tmpPath(String path2, [String path3 = '']) =>
path(tmpDirectory.path, path2, path3);
/// Finds package root in the parent chain of the current directory.
///
/// Recognizes package root by `pubspec.yaml` file.
Directory _findRootDir() {
var dir = Directory.current;
while (true) {
var pubspec = File('${dir.path}${Platform.pathSeparator}pubspec.yaml');
if (pubspec.existsSync()) return dir;
var parent = dir.parent;
if (dir.path == parent.path) {
throw UnsupportedError(
'Cannot find package root directory. Run tools from inside package!',
);
}
}
}
/// Leading-zero padding.
String lz(int n, [int length = 2]) => n.toString().padLeft(length, '0');
final _unsafeCharsRE = RegExp(r'\W+');
// Convert URI path to safe file path.
String safePath(String uriPath) => uriPath.replaceAll(_unsafeCharsRE, '-');