pub.dart 5.59 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 The Chromium 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:async';

7 8
import 'package:meta/meta.dart';

9
import '../base/common.dart';
10
import '../base/file_system.dart';
11
import '../base/logger.dart';
12
import '../base/platform.dart';
13
import '../base/process.dart';
14
import '../base/utils.dart';
15
import '../cache.dart';
16
import '../globals.dart';
17
import 'sdk.dart';
18

19 20 21
bool _shouldRunPubGet({ File pubSpecYaml, File dotPackages }) {
  if (!dotPackages.existsSync())
    return true;
22
  final DateTime dotPackagesLastModified = dotPackages.lastModifiedSync();
23 24
  if (pubSpecYaml.lastModifiedSync().isAfter(dotPackagesLastModified))
    return true;
25
  final File flutterToolsStamp = Cache.instance.getStampFileFor('flutter_tools');
26 27
  if (flutterToolsStamp.existsSync() &&
      flutterToolsStamp.lastModifiedSync().isAfter(dotPackagesLastModified))
28 29 30 31
    return true;
  return false;
}

32
Future<Null> pubGet({
33
  String directory,
34 35
  bool skipIfAbsent: false,
  bool upgrade: false,
36
  bool offline: false,
37
  bool checkLastModified: true
38
}) async {
39
  directory ??= fs.currentDirectory.path;
40

41 42
  final File pubSpecYaml = fs.file(fs.path.join(directory, 'pubspec.yaml'));
  final File dotPackages = fs.file(fs.path.join(directory, '.packages'));
43 44

  if (!pubSpecYaml.existsSync()) {
45 46 47
    if (!skipIfAbsent)
      throwToolExit('$directory: no pubspec.yaml found');
    return;
48 49
  }

50
  if (!checkLastModified || _shouldRunPubGet(pubSpecYaml: pubSpecYaml, dotPackages: dotPackages)) {
51
    final String command = upgrade ? 'upgrade' : 'get';
52 53 54 55 56
    final Status status = logger.startProgress(
      'Running "flutter packages $command" in ${fs.path.basename(directory)}...',
      expectSlowOperation: true,
    );
    final List<String> args = <String>['--verbosity=warning', command, '--no-precompile'];
57 58
    if (offline)
      args.add('--offline');
59
    try {
60 61 62 63 64 65 66
      await pub(
        args,
        directory: directory,
        filter: _filterOverrideWarnings,
        failureMessage: 'pub $command failed',
        retry: true,
      );
67 68 69
    } finally {
      status.stop();
    }
70 71
  }

72 73
  if (!dotPackages.existsSync())
    throwToolExit('$directory: pub did not create .packages file');
74

75 76
  if (dotPackages.lastModifiedSync().isBefore(pubSpecYaml.lastModifiedSync()))
    throwToolExit('$directory: pub did not update .packages file (pubspec.yaml file has a newer timestamp)');
77
}
78

79 80
typedef String MessageFilter(String message);

81 82 83
/// Runs pub in 'batch' mode, forwarding complete lines written by pub to its
/// stdout/stderr streams to the corresponding stream of this process, optionally
/// applying filtering. The pub process will not receive anything on its stdin stream.
84 85 86
Future<Null> pub(List<String> arguments, {
  String directory,
  MessageFilter filter,
87 88
  String failureMessage: 'pub failed',
  @required bool retry,
89
}) async {
90 91 92 93 94 95
  int attempts = 0;
  int duration = 1;
  int code;
  while (true) {
    attempts += 1;
    code = await runCommandAndStreamOutput(
96
      _pubCommand(arguments),
97 98
      workingDirectory: directory,
      mapFunction: filter,
99
      environment: _pubEnvironment,
100 101 102 103 104 105 106 107 108
    );
    if (code != 69) // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart
      break;
    printStatus('$failureMessage ($code) -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...');
    await new Future<Null>.delayed(new Duration(seconds: duration));
    if (duration < 64)
      duration *= 2;
  }
  assert(code != null);
109 110 111 112
  if (code != 0)
    throwToolExit('$failureMessage ($code)', exitCode: code);
}

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
/// Runs pub in 'interactive' mode, directly piping the stdin stream of this
/// process to that of pub, and the stdout/stderr stream of pub to the corresponding
/// streams of this process.
Future<Null> pubInteractively(List<String> arguments, {
  String directory,
}) async {
  final int code = await runInteractively(
    _pubCommand(arguments),
    workingDirectory: directory,
    environment: _pubEnvironment,
  );
  if (code != 0)
    throwToolExit('pub finished with exit code $code', exitCode: code);
}

/// The command used for running pub.
List<String> _pubCommand(List<String> arguments) {
  return <String>[ sdkBinaryName('pub') ]..addAll(arguments);
}

/// The full environment used when running pub.
Map<String, String> get _pubEnvironment => <String, String>{
  'FLUTTER_ROOT': Cache.flutterRoot,
  _pubEnvironmentKey: _getPubEnvironmentValue(),
};

139
final RegExp _analyzerWarning = new RegExp(r'^! \w+ [^ ]+ from path \.\./\.\./bin/cache/dart-sdk/lib/\w+$');
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
/// The console environment key used by the pub tool.
const String _pubEnvironmentKey = 'PUB_ENVIRONMENT';

/// Returns the environment value that should be used when running pub.
///
/// Includes any existing environment variable, if one exists.
String _getPubEnvironmentValue() {
  final List<String> values = <String>[];

  final String existing = platform.environment[_pubEnvironmentKey];

  if ((existing != null) && existing.isNotEmpty) {
    values.add(existing);
  }

  if (isRunningOnBot) {
    values.add('flutter_bot');
  }

  values.add('flutter_cli');

  return values.join(':');
}

165
String _filterOverrideWarnings(String message) {
166
  // This function filters out these three messages:
167 168
  //   Warning: You are using these overridden dependencies:
  //   ! analyzer 0.29.0-alpha.0 from path ../../bin/cache/dart-sdk/lib/analyzer
169
  //   ! front_end 0.1.0-alpha.0 from path ../../bin/cache/dart-sdk/lib/front_end
170
  if (message == 'Warning: You are using these overridden dependencies:')
171
    return null;
172
  if (message.contains(_analyzerWarning))
173
    return null;
174
  return message;
175
}