@@ -80,6 +80,7 @@ abstract class Pub {
8080 @required Platform platform,
8181 @required BotDetector botDetector,
8282 @required Usage usage,
83+ File Function () toolStampFile,
8384 }) = _DefaultPub ;
8485
8586 /// Runs `pub get` .
@@ -92,6 +93,8 @@ abstract class Pub {
9293 bool skipIfAbsent = false ,
9394 bool upgrade = false ,
9495 bool offline = false ,
96+ bool checkLastModified = true ,
97+ bool skipPubspecYamlCheck = false ,
9598 bool generateSyntheticPackage = false ,
9699 String flutterRootOverride,
97100 });
@@ -138,7 +141,9 @@ class _DefaultPub implements Pub {
138141 @required Platform platform,
139142 @required BotDetector botDetector,
140143 @required Usage usage,
141- }) : _fileSystem = fileSystem,
144+ File Function () toolStampFile,
145+ }) : _toolStampFile = toolStampFile,
146+ _fileSystem = fileSystem,
142147 _logger = logger,
143148 _platform = platform,
144149 _botDetector = botDetector,
@@ -154,6 +159,7 @@ class _DefaultPub implements Pub {
154159 final Platform _platform;
155160 final BotDetector _botDetector;
156161 final Usage _usage;
162+ final File Function () _toolStampFile;
157163
158164 @override
159165 Future <void > get ({
@@ -162,52 +168,88 @@ class _DefaultPub implements Pub {
162168 bool skipIfAbsent = false ,
163169 bool upgrade = false ,
164170 bool offline = false ,
171+ bool checkLastModified = true ,
172+ bool skipPubspecYamlCheck = false ,
165173 bool generateSyntheticPackage = false ,
166174 String flutterRootOverride,
167175 }) async {
168176 directory ?? = _fileSystem.currentDirectory.path;
177+
178+ final File pubSpecYaml = _fileSystem.file (
179+ _fileSystem.path.join (directory, 'pubspec.yaml' ));
169180 final File packageConfigFile = _fileSystem.file (
170181 _fileSystem.path.join (directory, '.dart_tool' , 'package_config.json' ));
171182 final Directory generatedDirectory = _fileSystem.directory (
172183 _fileSystem.path.join (directory, '.dart_tool' , 'flutter_gen' ));
173184
174- final String command = upgrade ? 'upgrade' : 'get' ;
175- final Status status = _logger.startProgress (
176- 'Running "flutter pub $command " in ${_fileSystem .path .basename (directory )}...' ,
177- timeout: const TimeoutConfiguration ().slowOperation,
178- );
179- final bool verbose = _logger.isVerbose;
180- final List <String > args = < String > [
181- if (verbose)
182- '--verbose'
183- else
184- '--verbosity=warning' ,
185- ...< String > [
186- command,
187- '--no-precompile' ,
188- ],
189- if (offline)
190- '--offline' ,
191- ];
192- try {
193- await batch (
194- args,
195- context: context,
196- directory: directory,
197- failureMessage: 'pub $command failed' ,
198- retry: true ,
199- flutterRootOverride: flutterRootOverride,
185+ if (! skipPubspecYamlCheck && ! pubSpecYaml.existsSync ()) {
186+ if (! skipIfAbsent) {
187+ throwToolExit ('$directory : no pubspec.yaml found' );
188+ }
189+ return ;
190+ }
191+
192+ final DateTime originalPubspecYamlModificationTime = pubSpecYaml.lastModifiedSync ();
193+
194+ if (! checkLastModified || _shouldRunPubGet (
195+ pubSpecYaml: pubSpecYaml,
196+ packageConfigFile: packageConfigFile,
197+ )) {
198+ final String command = upgrade ? 'upgrade' : 'get' ;
199+ final Status status = _logger.startProgress (
200+ 'Running "flutter pub $command " in ${_fileSystem .path .basename (directory )}...' ,
201+ timeout: const TimeoutConfiguration ().slowOperation,
200202 );
201- status.stop ();
202- // The exception is rethrown, so don't catch only Exceptions.
203- } catch (exception) { // ignore: avoid_catches_without_on_clauses
204- status.cancel ();
205- rethrow ;
203+ final bool verbose = _logger.isVerbose;
204+ final List <String > args = < String > [
205+ if (verbose)
206+ '--verbose'
207+ else
208+ '--verbosity=warning' ,
209+ ...< String > [
210+ command,
211+ '--no-precompile' ,
212+ ],
213+ if (offline)
214+ '--offline' ,
215+ ];
216+ try {
217+ await batch (
218+ args,
219+ context: context,
220+ directory: directory,
221+ failureMessage: 'pub $command failed' ,
222+ retry: true ,
223+ flutterRootOverride: flutterRootOverride,
224+ );
225+ status.stop ();
226+ // The exception is rethrown, so don't catch only Exceptions.
227+ } catch (exception) { // ignore: avoid_catches_without_on_clauses
228+ status.cancel ();
229+ rethrow ;
230+ }
206231 }
207232
208233 if (! packageConfigFile.existsSync ()) {
209234 throwToolExit ('$directory : pub did not create .dart_tools/package_config.json file.' );
210235 }
236+ if (pubSpecYaml.lastModifiedSync () != originalPubspecYamlModificationTime) {
237+ throwToolExit (
238+ '$directory : unexpected concurrent modification of '
239+ 'pubspec.yaml while running pub.' );
240+ }
241+ // We don't check if dotPackages was actually modified, because as far as we can tell sometimes
242+ // pub will decide it does not need to actually modify it.
243+ final DateTime now = DateTime .now ();
244+ if (now.isBefore (originalPubspecYamlModificationTime)) {
245+ _logger.printError (
246+ 'Warning: File "${_fileSystem .path .absolute (pubSpecYaml .path )}" was created in the future. '
247+ 'Optimizations that rely on comparing time stamps will be unreliable. Check your '
248+ 'system clock for accuracy.\n '
249+ 'The timestamp was: $originalPubspecYamlModificationTime \n '
250+ 'The time now is: $now '
251+ );
252+ }
211253 await _updatePackageConfig (
212254 packageConfigFile,
213255 generatedDirectory,
@@ -350,6 +392,23 @@ class _DefaultPub implements Pub {
350392 return < String > [sdkPath, ...arguments];
351393 }
352394
395+ bool _shouldRunPubGet ({ @required File pubSpecYaml, @required File packageConfigFile }) {
396+ if (! packageConfigFile.existsSync ()) {
397+ return true ;
398+ }
399+ final DateTime dotPackagesLastModified = packageConfigFile.lastModifiedSync ();
400+ if (pubSpecYaml.lastModifiedSync ().isAfter (dotPackagesLastModified)) {
401+ return true ;
402+ }
403+ final File toolStampFile = _toolStampFile != null ? _toolStampFile () : null ;
404+ if (toolStampFile != null &&
405+ toolStampFile.existsSync () &&
406+ toolStampFile.lastModifiedSync ().isAfter (dotPackagesLastModified)) {
407+ return true ;
408+ }
409+ return false ;
410+ }
411+
353412 // Returns the environment value that should be used when running pub.
354413 //
355414 // Includes any existing environment variable, if one exists.
0 commit comments