|  | // 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. | 
|  |  | 
|  | /// Make `n` copies of flutter_gallery. | 
|  |  | 
|  | import 'dart:io'; | 
|  |  | 
|  | import 'package:args/args.dart'; | 
|  | import 'package:path/path.dart' as path; | 
|  |  | 
|  | /// If no `copies` param is passed in, we scale the generated app up to 60k lines. | 
|  | const int kTargetLineCount = 60 * 1024; | 
|  |  | 
|  | void main(List<String> args) { | 
|  | // If we're run from the `tools` dir, set the cwd to the repo root. | 
|  | if (path.basename(Directory.current.path) == 'tools') | 
|  | Directory.current = Directory.current.parent.parent; | 
|  |  | 
|  | final ArgParser argParser = ArgParser(); | 
|  | argParser.addOption('out'); | 
|  | argParser.addOption('copies'); | 
|  | argParser.addFlag('delete', negatable: false); | 
|  | argParser.addFlag('help', abbr: 'h', negatable: false); | 
|  |  | 
|  | final ArgResults results = argParser.parse(args); | 
|  |  | 
|  | if (results['help'] as bool) { | 
|  | print('Generate n copies of flutter_gallery.\n'); | 
|  | print('usage: dart mega_gallery.dart <options>'); | 
|  | print(argParser.usage); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | final Directory source = Directory(_normalize('dev/integration_tests/flutter_gallery')); | 
|  | final Directory out = Directory(_normalize(results['out'] as String)); | 
|  |  | 
|  | if (results['delete'] as bool) { | 
|  | if (out.existsSync()) { | 
|  | print('Deleting ${out.path}'); | 
|  | out.deleteSync(recursive: true); | 
|  | } | 
|  |  | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | if (!results.wasParsed('out')) { | 
|  | print('The --out parameter is required.'); | 
|  | print(argParser.usage); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | int copies; | 
|  | if (!results.wasParsed('copies')) { | 
|  | final SourceStats stats = getStatsFor(_dir(source, 'lib')); | 
|  | copies = (kTargetLineCount / stats.lines).round(); | 
|  | } else { | 
|  | copies = int.parse(results['copies'] as String); | 
|  | } | 
|  |  | 
|  | print('Making $copies copies of flutter_gallery.'); | 
|  | print(''); | 
|  | print('Stats:'); | 
|  | print('  packages/flutter            : ${getStatsFor(Directory("packages/flutter"))}'); | 
|  | print('  dev/integration_tests/flutter_gallery    : ${getStatsFor(Directory("dev/integration_tests/flutter_gallery"))}'); | 
|  |  | 
|  | final Directory lib = _dir(out, 'lib'); | 
|  | if (lib.existsSync()) | 
|  | lib.deleteSync(recursive: true); | 
|  |  | 
|  | // Copy everything that's not a symlink, dot directory, or build/. | 
|  | _copy(source, out); | 
|  |  | 
|  | // Make n - 1 copies. | 
|  | for (int i = 1; i < copies; i++) | 
|  | _copyGallery(out, i); | 
|  |  | 
|  | // Create a new entry-point. | 
|  | _createEntry(_file(out, 'lib/main.dart'), copies); | 
|  |  | 
|  | // Update the pubspec. | 
|  | String pubspec = _file(out, 'pubspec.yaml').readAsStringSync(); | 
|  | pubspec = pubspec.replaceAll('../../packages/flutter', '../../../packages/flutter'); | 
|  | _file(out, 'pubspec.yaml').writeAsStringSync(pubspec); | 
|  |  | 
|  | // Remove the (flutter_gallery specific) analysis_options.yaml file. | 
|  | _file(out, 'analysis_options.yaml').deleteSync(); | 
|  |  | 
|  | _file(out, '.dartignore').writeAsStringSync(''); | 
|  |  | 
|  | // Count source lines and number of files; tell how to run it. | 
|  | print('  ${path.relative(results["out"] as String)} : ${getStatsFor(out)}'); | 
|  | } | 
|  |  | 
|  | // TODO(devoncarew): Create an entry-point that builds a UI with all `n` copies. | 
|  | void _createEntry(File mainFile, int copies) { | 
|  | final StringBuffer imports = StringBuffer(); | 
|  |  | 
|  | for (int i = 1; i < copies; i++) { | 
|  | imports.writeln('// ignore: unused_import'); | 
|  | imports.writeln("import 'gallery_$i/main.dart' as main_$i;"); | 
|  | } | 
|  |  | 
|  | final String contents = ''' | 
|  | // 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:flutter/widgets.dart'; | 
|  |  | 
|  | import 'gallery/app.dart'; | 
|  | ${imports.toString().trim()} | 
|  |  | 
|  | void main() { | 
|  | runApp(const GalleryApp()); | 
|  | } | 
|  | '''; | 
|  |  | 
|  | mainFile.writeAsStringSync(contents); | 
|  | } | 
|  |  | 
|  | void _copyGallery(Directory galleryDir, int index) { | 
|  | final Directory lib = _dir(galleryDir, 'lib'); | 
|  | final Directory dest = _dir(lib, 'gallery_$index'); | 
|  | dest.createSync(); | 
|  |  | 
|  | // Copy demo/, gallery/, and main.dart. | 
|  | _copy(_dir(lib, 'demo'), _dir(dest, 'demo')); | 
|  | _copy(_dir(lib, 'gallery'), _dir(dest, 'gallery')); | 
|  | _file(dest, 'main.dart').writeAsBytesSync(_file(lib, 'main.dart').readAsBytesSync()); | 
|  | } | 
|  |  | 
|  | void _copy(Directory source, Directory target) { | 
|  | if (!target.existsSync()) | 
|  | target.createSync(recursive: true); | 
|  |  | 
|  | for (final FileSystemEntity entity in source.listSync(followLinks: false)) { | 
|  | final String name = path.basename(entity.path); | 
|  |  | 
|  | if (entity is Directory) { | 
|  | if (name == 'build' || name.startsWith('.')) | 
|  | continue; | 
|  | _copy(entity, Directory(path.join(target.path, name))); | 
|  | } else if (entity is File) { | 
|  | if (name == '.packages' || name == 'pubspec.lock') | 
|  | continue; | 
|  | final File dest = File(path.join(target.path, name)); | 
|  | dest.writeAsBytesSync(entity.readAsBytesSync()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Directory _dir(Directory parent, String name) => Directory(path.join(parent.path, name)); | 
|  | File _file(Directory parent, String name) => File(path.join(parent.path, name)); | 
|  | String _normalize(String filePath) => path.normalize(path.absolute(filePath)); | 
|  |  | 
|  | class SourceStats { | 
|  | int files = 0; | 
|  | int lines = 0; | 
|  |  | 
|  | @override | 
|  | String toString() => '${_comma(files).padLeft(3)} files, ${_comma(lines).padLeft(6)} lines'; | 
|  | } | 
|  |  | 
|  | SourceStats getStatsFor(Directory dir, [SourceStats stats]) { | 
|  | stats ??= SourceStats(); | 
|  |  | 
|  | for (final FileSystemEntity entity in dir.listSync(recursive: false, followLinks: false)) { | 
|  | final String name = path.basename(entity.path); | 
|  | if (entity is File && name.endsWith('.dart')) { | 
|  | stats.files += 1; | 
|  | stats.lines += _lineCount(entity); | 
|  | } else if (entity is Directory && !name.startsWith('.')) { | 
|  | getStatsFor(entity, stats); | 
|  | } | 
|  | } | 
|  |  | 
|  | return stats; | 
|  | } | 
|  |  | 
|  | int _lineCount(File file) { | 
|  | return file.readAsLinesSync().where((String line) { | 
|  | line = line.trim(); | 
|  | if (line.isEmpty || line.startsWith('//')) | 
|  | return false; | 
|  | return true; | 
|  | }).length; | 
|  | } | 
|  |  | 
|  | String _comma(int count) { | 
|  | final String str = count.toString(); | 
|  | if (str.length > 3) | 
|  | return str.substring(0, str.length - 3) + ',' + str.substring(str.length - 3); | 
|  | return str; | 
|  | } |