1// Copyright 2016 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/// Make `n` copies of flutter_gallery. 6 7import 'dart:io'; 8 9import 'package:args/args.dart'; 10import 'package:path/path.dart' as path; 11 12/// If no `copies` param is passed in, we scale the generated app up to 60k lines. 13const int kTargetLineCount = 60 * 1024; 14 15void main(List<String> args) { 16 // If we're run from the `tools` dir, set the cwd to the repo root. 17 if (path.basename(Directory.current.path) == 'tools') 18 Directory.current = Directory.current.parent.parent; 19 20 final ArgParser argParser = ArgParser(); 21 argParser.addOption('out'); 22 argParser.addOption('copies'); 23 argParser.addFlag('delete', negatable: false); 24 argParser.addFlag('help', abbr: 'h', negatable: false); 25 26 final ArgResults results = argParser.parse(args); 27 28 if (results['help']) { 29 print('Generate n copies of flutter_gallery.\n'); 30 print('usage: dart mega_gallery.dart <options>'); 31 print(argParser.usage); 32 exit(0); 33 } 34 35 final Directory source = Directory(_normalize('examples/flutter_gallery')); 36 final Directory out = Directory(_normalize(results['out'])); 37 38 if (results['delete']) { 39 if (out.existsSync()) { 40 print('Deleting ${out.path}'); 41 out.deleteSync(recursive: true); 42 } 43 44 exit(0); 45 } 46 47 if (!results.wasParsed('out')) { 48 print('The --out parameter is required.'); 49 print(argParser.usage); 50 exit(1); 51 } 52 53 int copies; 54 if (!results.wasParsed('copies')) { 55 final SourceStats stats = getStatsFor(_dir(source, 'lib')); 56 copies = (kTargetLineCount / stats.lines).round(); 57 } else { 58 copies = int.parse(results['copies']); 59 } 60 61 print('Making $copies copies of flutter_gallery.'); 62 print(''); 63 print('Stats:'); 64 print(' packages/flutter : ${getStatsFor(Directory("packages/flutter"))}'); 65 print(' examples/flutter_gallery : ${getStatsFor(Directory("examples/flutter_gallery"))}'); 66 67 final Directory lib = _dir(out, 'lib'); 68 if (lib.existsSync()) 69 lib.deleteSync(recursive: true); 70 71 // Copy everything that's not a symlink, dot directory, or build/. 72 _copy(source, out); 73 74 // Make n - 1 copies. 75 for (int i = 1; i < copies; i++) 76 _copyGallery(out, i); 77 78 // Create a new entry-point. 79 _createEntry(_file(out, 'lib/main.dart'), copies); 80 81 // Update the pubspec. 82 String pubspec = _file(out, 'pubspec.yaml').readAsStringSync(); 83 pubspec = pubspec.replaceAll('../../packages/flutter', '../../../packages/flutter'); 84 _file(out, 'pubspec.yaml').writeAsStringSync(pubspec); 85 86 // Remove the (flutter_gallery specific) analysis_options.yaml file. 87 _file(out, 'analysis_options.yaml').deleteSync(); 88 89 _file(out, '.dartignore').writeAsStringSync(''); 90 91 // Count source lines and number of files; tell how to run it. 92 print(' ${path.relative(results["out"])} : ${getStatsFor(out)}'); 93} 94 95// TODO(devoncarew): Create an entry-point that builds a UI with all `n` copies. 96void _createEntry(File mainFile, int copies) { 97 final StringBuffer imports = StringBuffer(); 98 99 for (int i = 1; i < copies; i++) { 100 imports.writeln('// ignore: unused_import'); 101 imports.writeln("import 'gallery_$i/main.dart' as main_$i;"); 102 } 103 104 final String contents = ''' 105// Copyright 2016 The Chromium Authors. All rights reserved. 106// Use of this source code is governed by a BSD-style license that can be 107// found in the LICENSE file. 108 109import 'package:flutter/widgets.dart'; 110 111import 'gallery/app.dart'; 112${imports.toString().trim()} 113 114void main() { 115 runApp(const GalleryApp()); 116} 117'''; 118 119 mainFile.writeAsStringSync(contents); 120} 121 122void _copyGallery(Directory galleryDir, int index) { 123 final Directory lib = _dir(galleryDir, 'lib'); 124 final Directory dest = _dir(lib, 'gallery_$index'); 125 dest.createSync(); 126 127 // Copy demo/, gallery/, and main.dart. 128 _copy(_dir(lib, 'demo'), _dir(dest, 'demo')); 129 _copy(_dir(lib, 'gallery'), _dir(dest, 'gallery')); 130 _file(dest, 'main.dart').writeAsBytesSync(_file(lib, 'main.dart').readAsBytesSync()); 131} 132 133void _copy(Directory source, Directory target) { 134 if (!target.existsSync()) 135 target.createSync(recursive: true); 136 137 for (FileSystemEntity entity in source.listSync(followLinks: false)) { 138 final String name = path.basename(entity.path); 139 140 if (entity is Directory) { 141 if (name == 'build' || name.startsWith('.')) 142 continue; 143 _copy(entity, Directory(path.join(target.path, name))); 144 } else if (entity is File) { 145 if (name == '.packages' || name == 'pubspec.lock') 146 continue; 147 final File dest = File(path.join(target.path, name)); 148 dest.writeAsBytesSync(entity.readAsBytesSync()); 149 } 150 } 151} 152 153Directory _dir(Directory parent, String name) => Directory(path.join(parent.path, name)); 154File _file(Directory parent, String name) => File(path.join(parent.path, name)); 155String _normalize(String filePath) => path.normalize(path.absolute(filePath)); 156 157class SourceStats { 158 int files = 0; 159 int lines = 0; 160 161 @override 162 String toString() => '${_comma(files).padLeft(3)} files, ${_comma(lines).padLeft(6)} lines'; 163} 164 165SourceStats getStatsFor(Directory dir, [SourceStats stats]) { 166 stats ??= SourceStats(); 167 168 for (FileSystemEntity entity in dir.listSync(recursive: false, followLinks: false)) { 169 final String name = path.basename(entity.path); 170 if (entity is File && name.endsWith('.dart')) { 171 stats.files += 1; 172 stats.lines += _lineCount(entity); 173 } else if (entity is Directory && !name.startsWith('.')) { 174 getStatsFor(entity, stats); 175 } 176 } 177 178 return stats; 179} 180 181int _lineCount(File file) { 182 return file.readAsLinesSync().where((String line) { 183 line = line.trim(); 184 if (line.isEmpty || line.startsWith('//')) 185 return false; 186 return true; 187 }).length; 188} 189 190String _comma(int count) { 191 final String str = count.toString(); 192 if (str.length > 3) 193 return str.substring(0, str.length - 3) + ',' + str.substring(str.length - 3); 194 return str; 195} 196