// 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 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 Reads the file `resources.g.dart` from the appropriate directory, extracts the embedded resource named ``, 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; } }