| // 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 'dart:io'; |
| |
| void _validate(List<String> args) { |
| bool errors = false; |
| if (!File('bin/internal/engine.version').existsSync()) { |
| errors = true; |
| print('This program must be run from the root of your flutter repository.'); |
| } |
| if (!File('../engine/src/flutter/DEPS').existsSync()) { |
| errors = true; |
| print('This program assumes the engine directory is a sibling to the flutter repository directory.'); |
| } |
| if (args.length != 1) { |
| errors = true; |
| print('This program takes the engine revision as a single argument.'); |
| } |
| if (errors) { |
| exit(-1); |
| } |
| } |
| |
| const String engineRepo = '../engine/src/flutter'; |
| |
| Future<void> main(List<String> args) async { |
| _validate(args); |
| await _fetchUpstream(); |
| await _fetchUpstream(engineRepo); |
| String flutterRevision; |
| await for (final FlutterEngineRevision revision in _logEngineVersions()) { |
| if (!await containsRevision(args[0], revision.engineRevision)) { |
| if (flutterRevision == null) { |
| print('Revision not found.'); |
| exit(-1); |
| } |
| print('earliest revision: $flutterRevision'); |
| print('Tags that contain this engine revision:'); |
| print(await _tagsForRevision(flutterRevision)); |
| exit(0); |
| } |
| flutterRevision = revision.flutterRevision; |
| } |
| } |
| |
| Future<void> _fetchUpstream([String workingDirectory = '.']) async { |
| print('Fetching remotes for "$workingDirectory" - you may be prompted for SSH credentials by git.'); |
| final ProcessResult fetchResult = await Process.run( |
| 'git', |
| <String>[ |
| 'fetch', |
| '--all', |
| ], |
| workingDirectory: workingDirectory, |
| ); |
| if (fetchResult.exitCode != 0) { |
| throw Exception('Failed to fetch upstream in repository $workingDirectory'); |
| } |
| } |
| |
| Future<String> _tagsForRevision(String flutterRevision) async { |
| final ProcessResult tagResult = await Process.run( |
| 'git', |
| <String>[ |
| 'tag', |
| '--contains', |
| flutterRevision, |
| ], |
| ); |
| return tagResult.stdout as String; |
| } |
| |
| Future<bool> containsRevision(String ancestorRevision, String revision) async { |
| final ProcessResult result = await Process.run( |
| 'git', |
| <String>[ |
| 'merge-base', |
| '--is-ancestor', |
| ancestorRevision, |
| revision, |
| ], |
| workingDirectory: engineRepo, |
| ); |
| return result.exitCode == 0; |
| } |
| |
| Stream<FlutterEngineRevision> _logEngineVersions() async* { |
| final ProcessResult result = await Process.run( |
| 'git', |
| <String>[ |
| 'log', |
| '--oneline', |
| '-p', |
| '--', |
| 'bin/internal/engine.version', |
| ], |
| ); |
| if (result.exitCode != 0) { |
| print(result.stderr); |
| throw Exception('Failed to log bin/internal/engine.version'); |
| } |
| |
| final List<String> lines = (result.stdout as String).split('\n'); |
| int index = 0; |
| while (index < lines.length - 1) { |
| final String flutterRevision = lines[index].split(' ').first; |
| index += 1; |
| while (!lines[index].startsWith('+') || lines[index].startsWith('+++')) { |
| index += 1; |
| } |
| if (index >= lines.length) { |
| break; |
| } |
| final String engineRevision = lines[index].substring(1); |
| yield FlutterEngineRevision(flutterRevision, engineRevision); |
| index += lines[index + 1].startsWith(r'\ ') ? 2 : 1; |
| } |
| } |
| |
| class FlutterEngineRevision { |
| const FlutterEngineRevision(this.flutterRevision, this.engineRevision); |
| |
| final String flutterRevision; |
| final String engineRevision; |
| |
| @override |
| String toString() => '$flutterRevision: $engineRevision'; |
| } |