Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: tools/migration/bin/migrate_batch.dart

Issue 2988973002: Better support for re-running in the middle of a migration. (Closed)
Patch Set: Better support for re-running in the middle of a migration. Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/migration/lib/src/fork.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// Given the beginning and ending file names in a batch, does as much automated 5 /// Given the beginning and ending file names in a batch, does as much automated
6 /// migration and possible and prints out the remaining manual steps required. 6 /// migration and possible and prints out the remaining manual steps required.
7 /// 7 ///
8 /// This should be safe to run, and safe to re-run on an in-progress chunk. 8 /// This should be safe to run, and safe to re-run on an in-progress chunk.
9 /// However, it has not been thoroughly tested, so run at your own risk. 9 /// However, it has not been thoroughly tested, so run at your own risk.
10 10
11 import 'dart:io'; 11 import 'dart:io';
12 12
13 import 'package:path/path.dart' as p; 13 import 'package:path/path.dart' as p;
14
15 import 'package:migration/src/log.dart';
16 import 'package:migration/src/validate.dart';
17 import 'package:status_file/status_file.dart'; 14 import 'package:status_file/status_file.dart';
18 15
16 import 'package:migration/src/fork.dart';
17 import 'package:migration/src/io.dart';
18 import 'package:migration/src/log.dart';
19
19 const simpleDirs = const ["corelib", "language", "lib"]; 20 const simpleDirs = const ["corelib", "language", "lib"];
20 21
21 final String sdkRoot =
22 p.normalize(p.join(p.dirname(p.fromUri(Platform.script)), '../../../'));
23
24 final String testRoot = p.join(sdkRoot, "tests");
25
26 bool dryRun = false;
27
28 void main(List<String> arguments) { 22 void main(List<String> arguments) {
29 if (arguments.contains("--dry-run")) { 23 if (arguments.contains("--dry-run")) {
30 dryRun = true; 24 dryRun = true;
31 arguments = arguments.where((argument) => argument != "--dry-run").toList(); 25 arguments = arguments.where((argument) => argument != "--dry-run").toList();
32 } 26 }
33 27
34 if (arguments.length != 2) { 28 if (arguments.length != 2) {
35 stderr.writeln( 29 stderr.writeln(
36 "Usage: dart migrate_batch.dart [--dry-run] <first file> <last file>"); 30 "Usage: dart migrate_batch.dart [--dry-run] <first file> <last file>");
37 stderr.writeln(); 31 stderr.writeln();
38 stderr.writeln("Example:"); 32 stderr.writeln("Example:");
39 stderr.writeln(); 33 stderr.writeln();
40 stderr.writeln( 34 stderr.writeln(
41 " \$ dart migrate_batch.dart corelib/map_to_string corelib/queue"); 35 " \$ dart migrate_batch.dart corelib/map_to_string corelib/queue");
42 exit(1); 36 exit(1);
43 } 37 }
44 38
45 var first = toTwoPath(arguments[0]);
46 var last = toTwoPath(arguments[1]);
47
48 var tests = scanTests(); 39 var tests = scanTests();
49 40
50 // Find the range of files in the chunk. We use comparisons here instead of 41 var startIndex = findFork(tests, arguments[0]);
51 // equality to try to compensate for files that may only appear in one fork 42 var endIndex = findFork(tests, arguments[1]);
52 // and should be part of the chunk but aren't officially listed as the begin
53 // or end point.
54 var startIndex = -1;
55 var endIndex = 0;
56 for (var i = 0; i < tests.length; i++) {
57 if (startIndex == -1 && tests[i].twoPath.compareTo(first) >= 0) {
58 startIndex = i;
59 }
60 43
61 if (tests[i].twoPath.compareTo(last) > 0) { 44 if (startIndex == null || endIndex == null) exit(1);
62 endIndex = i;
63 break;
64 }
65 }
66 45
67 if ((endIndex - startIndex) == 0) { 46 var first = tests[startIndex].twoPath;
47 var last = tests[endIndex].twoPath;
48
49 // Make the range half-inclusive to simplify the math below.
50 endIndex++;
51
52 if (endIndex - startIndex == 0) {
68 print(bold("No tests in range.")); 53 print(bold("No tests in range."));
69 return; 54 return;
70 } 55 }
71 56
72 print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} " 57 print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} "
73 "to ${bold(last)}..."); 58 "to ${bold(last)}...");
74 print(""); 59 print("");
75 60
76 var allTodos = <String, List<String>>{}; 61 var allTodos = <String, List<String>>{};
77 tests = tests.sublist(startIndex, endIndex); 62 tests = tests.sublist(startIndex, endIndex);
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 if (currentSection != null) { 130 if (currentSection != null) {
146 filteredStatusFile.sections.add(currentSection); 131 filteredStatusFile.sections.add(currentSection);
147 } 132 }
148 } 133 }
149 if (!filteredStatusFile.isEmpty) { 134 if (!filteredStatusFile.isEmpty) {
150 statusFileEntries.writeln("Entries for status file ${statusFile.path}:"); 135 statusFileEntries.writeln("Entries for status file ${statusFile.path}:");
151 statusFileEntries.writeln(filteredStatusFile); 136 statusFileEntries.writeln(filteredStatusFile);
152 } 137 }
153 } 138 }
154 139
155 String toTwoPath(String path) { 140 int findFork(List<Fork> forks, String description) {
156 // Allow eliding "_test" and/or ".dart" to make things more command-line 141 var matches = <int>[];
157 // friendly. 142
158 if (!path.endsWith(".dart") && !path.endsWith("_test.dart")) { 143 for (var i = 0; i < forks.length; i++) {
159 path += "_test.dart"; 144 if (forks[i].twoPath.contains(description)) matches.add(i);
160 } 145 }
161 if (!path.endsWith(".dart")) path += ".dart";
162 146
163 for (var dir in simpleDirs) { 147 if (matches.isEmpty) {
164 if (p.isWithin(dir, path)) { 148 print('Could not find a test matching "${bold(description)}".');
165 return p.join("${dir}_2", p.relative(path, from: dir)); 149 return null;
150 } else if (matches.length == 1) {
151 return matches.first;
152 } else {
153 print('Description "${bold(description)}" is ambiguous. Could be any of:');
154 for (var i in matches) {
155 print("- ${forks[i].twoPath.replaceAll(description, bold(description))}");
166 } 156 }
167 157
168 if (p.isWithin("${dir}_strong", path)) { 158 print("Please use a more precise description.");
169 return p.join("${dir}_2", p.relative(path, from: dir)); 159 return null;
170 }
171 } 160 }
172
173 if (p.isWithin("html", path)) {
174 return p.join("lib_2/html", p.relative(path, from: "html"));
175 }
176
177 if (p.isWithin("isolate", path)) {
178 return p.join("lib_2/isolate", p.relative(path, from: "isolate"));
179 }
180
181 // Guess it's already a two path.
182 return path;
183 } 161 }
184 162
185 /// Loads all of the unforked test files. 163 /// Loads all of the unforked test files.
186 /// 164 ///
187 /// Creates an list of [Fork]s, ordered by their destination paths. Handles 165 /// Creates an list of [Fork]s, ordered by their destination paths. Handles
188 /// tests that only appear in one fork or the other, or both. 166 /// tests that only appear in one fork or the other, or both.
189 List<Fork> scanTests() { 167 List<Fork> scanTests() {
190 var tests = <String, Fork>{}; 168 var tests = <String, Fork>{};
191 169
192 addTestDirectory(String fromDir, String twoDir) { 170 addFromDirectory(String fromDir, String twoDir) {
193 for (var entry 171 for (var path in listFiles(fromDir)) {
194 in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) { 172 var fromPath = p.relative(path, from: testRoot);
195 if (!entry.path.endsWith(".dart")) continue;
196
197 var fromPath = p.relative(entry.path, from: testRoot);
198 var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir)); 173 var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir));
199 174
200 var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath)); 175 var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath));
201 if (fromDir.contains("_strong")) { 176 if (fromDir.contains("_strong")) {
202 fork.strongPath = fromPath; 177 fork.strongPath = fromPath;
203 } else { 178 } else {
204 fork.onePath = fromPath; 179 fork.onePath = fromPath;
205 } 180 }
206 } 181 }
207 } 182 }
208 183
209 addTestDirectory("corelib", "corelib_2"); 184 addFromDirectory("corelib", "corelib_2");
210 addTestDirectory("corelib_strong", "corelib_2"); 185 addFromDirectory("corelib_strong", "corelib_2");
211 addTestDirectory("html", "lib_2/html"); 186 addFromDirectory("html", "lib_2/html");
212 addTestDirectory("isolate", "lib_2/isolate"); 187 addFromDirectory("isolate", "lib_2/isolate");
213 addTestDirectory("language", "language_2"); 188 addFromDirectory("language", "language_2");
214 addTestDirectory("language_strong", "language_2"); 189 addFromDirectory("language_strong", "language_2");
215 addTestDirectory("lib", "lib_2"); 190 addFromDirectory("lib", "lib_2");
216 addTestDirectory("lib_strong", "lib_2"); 191 addFromDirectory("lib_strong", "lib_2");
192
193 // Include tests that have already been migrated too so we can show what
194 // works remains to be done in them.
195 const twoDirs = const [
196 "corelib_2",
197 "lib_2",
198 "language_2",
199 ];
200
201 for (var dir in twoDirs) {
202 for (var path in listFiles(dir)) {
203 var twoPath = p.relative(path, from: testRoot);
204 tests.putIfAbsent(twoPath, () => new Fork(twoPath));
205 }
206 }
217 207
218 var sorted = tests.values.toList(); 208 var sorted = tests.values.toList();
219 sorted.sort((a, b) => a.twoPath.compareTo(b.twoPath)); 209 sorted.sort((a, b) => a.twoPath.compareTo(b.twoPath));
220 return sorted; 210 return sorted;
221 } 211 }
222 212
223 List<StatusFile> loadStatusFiles() { 213 List<StatusFile> loadStatusFiles() {
224 var statusFiles = <StatusFile>[]; 214 var statusFiles = <StatusFile>[];
225 215
226 addStatusFile(String fromDir) { 216 addStatusFile(String fromDir) {
227 for (var entry 217 for (var path in listFiles(fromDir, extension: ".status")) {
228 in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) { 218 statusFiles.add(new StatusFile.read(path));
229 if (!entry.path.endsWith(".status")) continue;
230
231 statusFiles.add(new StatusFile.read(entry.path));
232 } 219 }
233 } 220 }
234 221
235 addStatusFile("corelib"); 222 addStatusFile("corelib");
236 addStatusFile("corelib_strong"); 223 addStatusFile("corelib_strong");
237 addStatusFile("html"); 224 addStatusFile("html");
238 addStatusFile("isolate"); 225 addStatusFile("isolate");
239 addStatusFile("language"); 226 addStatusFile("language");
240 addStatusFile("language_strong"); 227 addStatusFile("language_strong");
241 addStatusFile("lib"); 228 addStatusFile("lib");
242 addStatusFile("lib_strong"); 229 addStatusFile("lib_strong");
243 return statusFiles; 230 return statusFiles;
244 } 231 }
245
246 /// Moves the file from [from] to [to], which are both assumed to be relative
247 /// paths inside "tests".
248 void moveFile(String from, String to) {
249 if (dryRun) {
250 print(" Dry run: move $from to $to");
251 return;
252 }
253
254 // Create the directory if needed.
255 new Directory(p.dirname(p.join(testRoot, to))).createSync(recursive: true);
256
257 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to));
258 }
259
260 /// Reads the contents of the file at [path], which is assumed to be relative
261 /// within "tests".
262 String readFile(String path) {
263 return new File(p.join(testRoot, path)).readAsStringSync();
264 }
265
266 /// Deletes the file at [path], which is assumed to be relative within "tests".
267 void deleteFile(String path) {
268 if (dryRun) {
269 print(" Dry run: delete $path");
270 return;
271 }
272
273 new File(p.join(testRoot, path)).deleteSync();
274 }
275
276 class Fork {
277 final String twoPath;
278 String onePath;
279 String strongPath;
280
281 String get twoSource {
282 if (twoPath == null) return null;
283 if (_twoSource == null) _twoSource = readFile(twoPath);
284 return _twoSource;
285 }
286
287 String _twoSource;
288
289 String get oneSource {
290 if (onePath == null) return null;
291 if (_oneSource == null) _oneSource = readFile(onePath);
292 return _oneSource;
293 }
294
295 String _oneSource;
296
297 String get strongSource {
298 if (strongPath == null) return null;
299 if (_strongSource == null) _strongSource = readFile(strongPath);
300 return _strongSource;
301 }
302
303 String _strongSource;
304
305 Fork(this.twoPath);
306
307 List<String> migrate() {
308 print("- ${bold(twoPath)}:");
309
310 var todos = <String>[];
311 var isMigrated = new File(p.join(testRoot, twoPath)).existsSync();
312
313 // If there is a migrated version and it's the same as an unmigrated one,
314 // delete the unmigrated one.
315 if (isMigrated) {
316 if (onePath != null) {
317 if (oneSource == twoSource) {
318 deleteFile(onePath);
319 done("Deleted already-migrated $onePath.");
320 } else {
321 note("${bold(onePath)} does not match already-migrated "
322 "${bold(twoPath)}.");
323 todos.add("Merge from ${bold(onePath)} into this file.");
324 validateFile(onePath, oneSource);
325 }
326 }
327
328 if (strongPath != null) {
329 if (strongSource == twoSource) {
330 deleteFile(strongPath);
331 done("Deleted already-migrated ${bold(strongPath)}.");
332 } else {
333 note("${bold(strongPath)} does not match already-migrated "
334 "${bold(twoPath)}.");
335 todos.add("Merge from ${bold(strongPath)} into this file.");
336 validateFile(strongPath, strongSource);
337 }
338 }
339 } else {
340 // If it only exists in one place, just move it.
341 if (strongPath == null) {
342 moveFile(onePath, twoPath);
343 done("Moved from ${bold(onePath)} (no strong mode fork).");
344 } else if (onePath == null) {
345 moveFile(strongPath, twoPath);
346 done("Moved from ${bold(strongPath)} (no 1.0 mode fork).");
347 } else if (oneSource == strongSource) {
348 // The forks are identical, pick one.
349 moveFile(onePath, twoPath);
350 deleteFile(strongPath);
351 done("Merged identical forks.");
352 validateFile(twoPath, oneSource);
353 } else {
354 // Otherwise, a manual merge is required. Start with the strong one.
355 print(new File(strongPath).existsSync());
356 moveFile(strongPath, twoPath);
357 done("Moved strong fork, kept 1.0 fork, manual merge required.");
358 todos.add("Merge from ${bold(onePath)} into this file.");
359 validateFile(onePath, oneSource);
360 }
361 }
362
363 validateFile(twoPath, twoSource, todos);
364
365 return todos;
366 }
367 }
OLDNEW
« no previous file with comments | « no previous file | tools/migration/lib/src/fork.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698