Unverified Commit 5a85c0d8 authored by Kenzie (Schmoll) Davisson's avatar Kenzie (Schmoll) Davisson Committed by GitHub

Launch DevTools from the 'dart devtools' command instead of pub (#90894)

parent 15967669
...@@ -25,7 +25,3 @@ of the `flutter/plugins` repository to be used for testing. The ...@@ -25,7 +25,3 @@ of the `flutter/plugins` repository to be used for testing. The
`flutter/flutter`; it is only used as part of the test suite for `flutter/flutter`; it is only used as part of the test suite for
verification, and the pinned version here makes sure that tests are verification, and the pinned version here makes sure that tests are
deterministic at each `flutter/flutter` commit. deterministic at each `flutter/flutter` commit.
The `bin/internal/devtools.version` file specifies the version of the
`devtools` package on `pub` that should be activated when running the
Flutter command line tool.
...@@ -308,25 +308,6 @@ Future<void> _runIntegrationToolTests() async { ...@@ -308,25 +308,6 @@ Future<void> _runIntegrationToolTests() async {
.map<String>((FileSystemEntity entry) => path.relative(entry.path, from: toolsPath)) .map<String>((FileSystemEntity entry) => path.relative(entry.path, from: toolsPath))
.where((String testPath) => path.basename(testPath).endsWith('_test.dart')).toList(); .where((String testPath) => path.basename(testPath).endsWith('_test.dart')).toList();
// Make sure devtools is ready first, because that might take a while and we don't
// want any of the tests to time out while they themselves try to activate devtools.
final Map<String, String> pubEnvironment = <String, String>{
'FLUTTER_ROOT': flutterRoot,
};
if (Directory(pubCache).existsSync()) {
pubEnvironment['PUB_CACHE'] = pubCache;
}
await runCommand(
pub,
<String>[
'global',
'activate',
'devtools',
File(path.join(flutterRoot, 'bin', 'internal', 'devtools.version')).readAsStringSync(),
],
environment: pubEnvironment,
);
await _pubRunTest( await _pubRunTest(
toolsPath, toolsPath,
forceSingleCore: true, forceSingleCore: true,
......
...@@ -108,11 +108,8 @@ Future<void> main(List<String> args) async { ...@@ -108,11 +108,8 @@ Future<void> main(List<String> args) async {
// devtools source code. // devtools source code.
DevtoolsLauncher: () => DevtoolsServerLauncher( DevtoolsLauncher: () => DevtoolsServerLauncher(
processManager: globals.processManager, processManager: globals.processManager,
fileSystem: globals.fs,
dartExecutable: globals.artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, dartExecutable: globals.artifacts.getHostArtifact(HostArtifact.engineDartBinary).path,
logger: globals.logger, logger: globals.logger,
platform: globals.platform,
persistentToolState: globals.persistentToolState,
), ),
Logger: () { Logger: () {
final LoggerFactory loggerFactory = LoggerFactory( final LoggerFactory loggerFactory = LoggerFactory(
......
...@@ -215,11 +215,8 @@ Future<T> runInContext<T>( ...@@ -215,11 +215,8 @@ Future<T> runInContext<T>(
), ),
DevtoolsLauncher: () => DevtoolsServerLauncher( DevtoolsLauncher: () => DevtoolsServerLauncher(
processManager: globals.processManager, processManager: globals.processManager,
fileSystem: globals.fs,
dartExecutable: globals.artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, dartExecutable: globals.artifacts.getHostArtifact(HostArtifact.engineDartBinary).path,
logger: globals.logger, logger: globals.logger,
platform: globals.platform,
persistentToolState: globals.persistentToolState,
), ),
Doctor: () => Doctor(logger: globals.logger), Doctor: () => Doctor(logger: globals.logger),
DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance, DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance,
......
...@@ -9,55 +9,31 @@ import 'dart:async'; ...@@ -9,55 +9,31 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'base/file_system.dart';
import 'base/io.dart' as io; import 'base/io.dart' as io;
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/platform.dart';
import 'cache.dart';
import 'convert.dart'; import 'convert.dart';
import 'persistent_tool_state.dart';
import 'resident_runner.dart'; import 'resident_runner.dart';
/// An implementation of the devtools launcher that uses `pub global activate` to /// An implementation of the devtools launcher that uses `pub global activate` to
/// start a server instance. /// start a server instance.
class DevtoolsServerLauncher extends DevtoolsLauncher { class DevtoolsServerLauncher extends DevtoolsLauncher {
DevtoolsServerLauncher({ DevtoolsServerLauncher({
@required Platform platform,
@required ProcessManager processManager, @required ProcessManager processManager,
@required FileSystem fileSystem,
@required String dartExecutable, @required String dartExecutable,
@required Logger logger, @required Logger logger,
@required PersistentToolState persistentToolState,
@visibleForTesting io.HttpClient httpClient,
}) : _processManager = processManager, }) : _processManager = processManager,
_fileSystem = fileSystem,
_dartExecutable = dartExecutable, _dartExecutable = dartExecutable,
_logger = logger, _logger = logger;
_platform = platform,
_persistentToolState = persistentToolState,
_httpClient = httpClient ?? io.HttpClient();
final ProcessManager _processManager; final ProcessManager _processManager;
final FileSystem _fileSystem;
final String _dartExecutable; final String _dartExecutable;
final Logger _logger; final Logger _logger;
final Platform _platform;
final PersistentToolState _persistentToolState;
final io.HttpClient _httpClient;
final Completer<void> _processStartCompleter = Completer<void>(); final Completer<void> _processStartCompleter = Completer<void>();
io.Process _devToolsProcess; io.Process _devToolsProcess;
static final RegExp _serveDevToolsPattern = static final RegExp _serveDevToolsPattern =
RegExp(r'Serving DevTools at ((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+?)\.?$'); RegExp(r'Serving DevTools at ((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+?)\.?$');
static const String _pubHostedUrlKey = 'PUB_HOSTED_URL';
static String _devtoolsVersion;
static String devtoolsVersion(FileSystem fs) {
return _devtoolsVersion ??= fs.file(
fs.path.join(Cache.flutterRoot, 'bin', 'internal', 'devtools.version'),
).readAsStringSync();
}
@override @override
Future<void> get processStart => _processStartCompleter.future; Future<void> get processStart => _processStartCompleter.future;
...@@ -67,61 +43,8 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -67,61 +43,8 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
// Place this entire method in a try/catch that swallows exceptions because // Place this entire method in a try/catch that swallows exceptions because
// this method is guaranteed not to return a Future that throws. // this method is guaranteed not to return a Future that throws.
try { try {
bool offline = false;
bool useOverrideUrl = false;
try {
Uri uri;
if (_platform.environment.containsKey(_pubHostedUrlKey)) {
useOverrideUrl = true;
uri = Uri.parse(_platform.environment[_pubHostedUrlKey]);
} else {
uri = Uri.https('pub.dev', '');
}
final io.HttpClientRequest request = await _httpClient.headUrl(uri);
final io.HttpClientResponse response = await request.close();
await response.drain<void>();
if (response.statusCode != io.HttpStatus.ok) {
_logger.printTrace(
'Skipping devtools launch because pub.dev responded with HTTP '
'status code ${response.statusCode} instead of ${io.HttpStatus.ok}.',
);
offline = true;
}
} on Exception catch (e) {
_logger.printTrace(
'Skipping devtools launch because connecting to pub.dev failed with $e',
);
offline = true;
} on ArgumentError {
if (!useOverrideUrl) {
rethrow;
}
// The user supplied a custom pub URL that was invalid, pretend to be offline
// and inform them that the URL was invalid.
offline = true;
_logger.printError(
'PUB_HOSTED_URL was set to an invalid URL: "${_platform.environment[_pubHostedUrlKey]}".'
);
}
bool devToolsActive = await _checkForActiveDevTools();
if (!offline) {
await _activateDevTools(throttleUpdates: devToolsActive);
if (!devToolsActive) {
devToolsActive = await _checkForActiveDevTools();
}
}
if (!devToolsActive) {
// We don't have devtools installed and installing it failed;
// _activateDevTools will have reported the error already.
return;
}
_devToolsProcess = await _processManager.start(<String>[ _devToolsProcess = await _processManager.start(<String>[
_dartExecutable, _dartExecutable,
'pub',
'global',
'run',
'devtools', 'devtools',
'--no-launch-browser', '--no-launch-browser',
if (vmServiceUri != null) '--vm-uri=$vmServiceUri', if (vmServiceUri != null) '--vm-uri=$vmServiceUri',
...@@ -149,61 +72,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -149,61 +72,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
} }
} }
static final RegExp _devToolsInstalledPattern = RegExp(r'^devtools ', multiLine: true);
/// Check if the DevTools package is already active by running "pub global list".
Future<bool> _checkForActiveDevTools() async {
final io.ProcessResult _pubGlobalListProcess = await _processManager.run(
<String>[ _dartExecutable, 'pub', 'global', 'list' ],
);
return _pubGlobalListProcess.stdout.toString().contains(_devToolsInstalledPattern);
}
/// Helper method to activate the DevTools pub package.
///
/// If throttleUpdates is true, then this is a no-op if it was run in
/// the last twelve hours. It should be set to true if devtools is known
/// to already be installed.
///
/// Return value indicates if DevTools was installed or updated.
Future<bool> _activateDevTools({@required bool throttleUpdates}) async {
assert(throttleUpdates != null);
const Duration _throttleDuration = Duration(hours: 12);
if (throttleUpdates) {
if (_persistentToolState.lastDevToolsActivationTime != null &&
DateTime.now().difference(_persistentToolState.lastDevToolsActivationTime) < _throttleDuration) {
_logger.printTrace('DevTools activation throttled until ${_persistentToolState.lastDevToolsActivationTime.add(_throttleDuration).toLocal()}.');
return false; // Throttled.
}
}
final Status status = _logger.startProgress('Activating Dart DevTools...');
try {
final io.ProcessResult _devToolsActivateProcess = await _processManager
.run(<String>[
_dartExecutable,
'pub',
'global',
'activate',
'devtools',
devtoolsVersion(_fileSystem),
]);
if (_devToolsActivateProcess.exitCode != 0) {
_logger.printError(
'Error running `pub global activate devtools`:\n'
'${_devToolsActivateProcess.stderr}'
);
return false; // Failed to activate.
}
_persistentToolState.lastDevToolsActivation = DateTime.now();
return true; // Activation succeeded!
} on Exception catch (e, _) {
_logger.printError('Error running `pub global activate devtools`: $e');
return false;
} finally {
status.stop();
}
}
@override @override
Future<DevToolsServerAddress> serve() async { Future<DevToolsServerAddress> serve() async {
if (activeDevToolsServer == null) { if (activeDevToolsServer == null) {
......
...@@ -55,10 +55,6 @@ abstract class PersistentToolState { ...@@ -55,10 +55,6 @@ abstract class PersistentToolState {
/// Whether this client was already determined to be or not be a bot. /// Whether this client was already determined to be or not be a bot.
bool? get isRunningOnBot; bool? get isRunningOnBot;
set runningOnBot(bool value); // Enforced nonnull setter. set runningOnBot(bool value); // Enforced nonnull setter.
/// The last time the DevTools package was activated from pub.
DateTime? get lastDevToolsActivationTime;
set lastDevToolsActivation(DateTime value); // Enforced nonnull setter.
} }
class _DefaultPersistentToolState implements PersistentToolState { class _DefaultPersistentToolState implements PersistentToolState {
...@@ -92,7 +88,6 @@ class _DefaultPersistentToolState implements PersistentToolState { ...@@ -92,7 +88,6 @@ class _DefaultPersistentToolState implements PersistentToolState {
Channel.stable: 'last-active-stable-version' Channel.stable: 'last-active-stable-version'
}; };
static const String _kBotKey = 'is-bot'; static const String _kBotKey = 'is-bot';
static const String _kLastDevToolsActivationTimeKey = 'last-devtools-activation-time';
static const String _kLicenseHash = 'license-hash'; static const String _kLicenseHash = 'license-hash';
final Config _config; final Config _config;
...@@ -140,14 +135,4 @@ class _DefaultPersistentToolState implements PersistentToolState { ...@@ -140,14 +135,4 @@ class _DefaultPersistentToolState implements PersistentToolState {
set runningOnBot(bool value) { set runningOnBot(bool value) {
_config.setValue(_kBotKey, value); _config.setValue(_kBotKey, value);
} }
@override
DateTime? get lastDevToolsActivationTime {
final String? value = _config.getValue(_kLastDevToolsActivationTimeKey) as String?;
return value != null ? DateTime.parse(value) : null;
}
@override
set lastDevToolsActivation(DateTime time) =>
_config.setValue(_kLastDevToolsActivationTimeKey, time.toString());
} }
...@@ -57,23 +57,4 @@ void main() { ...@@ -57,23 +57,4 @@ void main() {
expect(state2.lastActiveVersion(Channel.beta), 'ghi'); expect(state2.lastActiveVersion(Channel.beta), 'ghi');
expect(state2.lastActiveVersion(Channel.stable), 'jkl'); expect(state2.lastActiveVersion(Channel.stable), 'jkl');
}); });
testWithoutContext('lastDevToolsActivationTime can be cached and stored', () {
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final Directory directory = fileSystem.directory('state_dir')..createSync();
final PersistentToolState state1 = PersistentToolState.test(
directory: directory,
logger: BufferLogger.test(),
);
final DateTime time = DateTime.now();
state1.lastDevToolsActivation = time;
final PersistentToolState state2 = PersistentToolState.test(
directory: directory,
logger: BufferLogger.test(),
);
expect(state2.lastDevToolsActivationTime, equals(time));
});
} }
...@@ -6,9 +6,7 @@ ...@@ -6,9 +6,7 @@
import 'dart:async'; import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
...@@ -78,10 +76,6 @@ final FakeVmServiceRequest listViews = FakeVmServiceRequest( ...@@ -78,10 +76,6 @@ final FakeVmServiceRequest listViews = FakeVmServiceRequest(
void main() { void main() {
Cache.flutterRoot = ''; Cache.flutterRoot = '';
final MemoryFileSystem fakefs = MemoryFileSystem.test()
..directory('bin').createSync()
..directory('bin/internal').createSync()
..file('bin/internal/devtools.version').writeAsStringSync('1.0.0');
testWithoutContext('Does not serve devtools if launcher is null', () async { testWithoutContext('Does not serve devtools if launcher is null', () async {
final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler( final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler(
...@@ -110,11 +104,8 @@ void main() { ...@@ -110,11 +104,8 @@ void main() {
testWithoutContext('Can use devtools with existing devtools URI', () async { testWithoutContext('Can use devtools with existing devtools URI', () async {
final DevtoolsServerLauncher launcher = DevtoolsServerLauncher( final DevtoolsServerLauncher launcher = DevtoolsServerLauncher(
processManager: FakeProcessManager.empty(), processManager: FakeProcessManager.empty(),
fileSystem: fakefs,
dartExecutable: 'dart', dartExecutable: 'dart',
logger: BufferLogger.test(), logger: BufferLogger.test(),
platform: FakePlatform(),
persistentToolState: null,
); );
final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler( final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler(
// Uses real devtools instance which should be a no-op if // Uses real devtools instance which should be a no-op if
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment