blob: 3b605936da1c625a0abd2f914ce12bb4cb4da815 [file] [log] [blame]
// Copyright (c) 2021, 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.
// This script provides a quick way, via the command line, to extract one of the
// resources encoded in the file lib/src/front_end/resources/resources.g.dart.
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:args/args.dart';
import 'package:path/path.dart' as path;
main(List<String> args) {
var argResults = argParser.parse(args);
if (argResults['help'] as bool) {
fail(null, showUsage: true);
}
if (argResults.rest.isNotEmpty) {
fail('Unexpected extra arguments', showUsage: true);
}
bool list = argResults['list'] as bool;
String path = argResults['path'] as String;
String resource = argResults['resource'] as String;
if (list && resource != null) {
fail('Only one of --resource and --list may be provided');
} else if (!list && resource == null) {
fail('Either --resource or --list must be provided');
}
var file = locateResourcesFile(path);
var parseResult =
parseString(content: file.readAsStringSync(), path: file.path);
final variableNameRegExp = RegExp(r'^_(.*)_base64$');
for (var declaration in parseResult.unit.declarations) {
if (declaration is TopLevelVariableDeclaration) {
for (var variable in declaration.variables.variables) {
if (variable.initializer == null) continue;
var match = variableNameRegExp.matchAsPrefix(variable.name.name);
if (match == null) continue;
var shortName = match.group(1);
if (list) {
stdout.writeln(shortName);
} else if (resource == shortName) {
stdout.add(decodeVariableDeclaration(variable));
return;
}
}
}
}
if (list) {
return;
} else {
fail('Resource $resource not found in ${file.path}');
}
}
final argParser = ArgParser()
..addOption('resource',
abbr: 'r', valueHelp: 'RESOURCE', help: 'Extract resource RESOURCE')
..addFlag('list',
negatable: false, abbr: 'l', help: 'List which resources are present')
..addOption('path',
abbr: 'p',
valueHelp: 'PATH',
help:
'Search for resources.g.dart inside PATH rather than current working '
'directory')
..addFlag('help', negatable: false, abbr: 'h');
Uint8List decodeVariableDeclaration(VariableDeclaration variable) {
var initializer = variable.initializer as StringLiteral;
var stringValue = initializer.stringValue;
return base64.decode(stringValue.replaceAll('\n', '').trim());
}
void fail(String message, {bool showUsage = false}) {
if (message != null) {
stderr.writeln(message);
}
if (showUsage) {
stderr.writeln('''
usage: dart pkg/nnbd_migration/tool/codegen/extract_resource.dart -r <resource>
Reads the file `resources.g.dart` from the appropriate directory, extracts the
embedded resource named `<resource>`, and prints it to standard out.
''');
stderr.writeln(argParser.usage);
}
exit(1);
}
/// Tries to guess the location of `resources.g.dart` using optional [pathHint]
/// as a starting point.
File locateResourcesFile(String pathHint) {
pathHint ??= '.';
final pathParts =
'pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart'
.split('/');
var currentPath = path.normalize(pathHint);
while (true) {
for (int i = 0; i <= pathParts.length; i++) {
var pathToTry = path.normalize(path.join(
currentPath, path.joinAll(pathParts.sublist(pathParts.length - i))));
var file = File(pathToTry);
var type = file.statSync().type;
if (type == FileSystemEntityType.notFound && i == 0) {
fail('No such file or directory: $pathToTry');
}
if (type == FileSystemEntityType.link) {
type = File(file.resolveSymbolicLinksSync()).statSync().type;
}
if (type == FileSystemEntityType.file) {
return file;
}
}
if (currentPath == '.') {
currentPath = Directory.current.path;
}
var nextPath = path.dirname(currentPath);
if (nextPath == currentPath) {
fail('Could not find file `resources.g.dart` starting at $pathHint');
}
currentPath = nextPath;
}
}