35
35
import io .flutter .bazel .Workspace ;
36
36
import io .flutter .bazel .WorkspaceCache ;
37
37
import io .flutter .font .FontPreviewProcessor ;
38
+ import io .flutter .pub .PubRoot ;
39
+ import io .flutter .pub .PubRoots ;
38
40
import io .flutter .settings .FlutterSettings ;
39
41
import org .jetbrains .annotations .Nls ;
40
42
import org .jetbrains .annotations .NotNull ;
46
48
import java .awt .datatransfer .StringSelection ;
47
49
import java .net .URI ;
48
50
import java .net .URISyntaxException ;
51
+ import java .util .List ;
52
+ import java .util .concurrent .Semaphore ;
49
53
50
54
// Note: when updating the settings here, update FlutterSearchableOptionContributor as well.
51
55
@@ -85,6 +89,13 @@ public class FlutterSettingsConfigurable implements SearchableConfigurable {
85
89
private boolean ignoringSdkChanges = false ;
86
90
87
91
private String fullVersionString ;
92
+ private FlutterSdkVersion previousSdkVersion ;
93
+
94
+ /**
95
+ * Semaphore used to synchronize flutter commands so we don't try to do two at once.
96
+ */
97
+ private final Semaphore lock = new Semaphore (1 , true );
98
+ private Process updater ;
88
99
89
100
FlutterSettingsConfigurable (@ NotNull Project project ) {
90
101
this .myProject = project ;
@@ -95,6 +106,10 @@ public class FlutterSettingsConfigurable implements SearchableConfigurable {
95
106
}
96
107
97
108
private void init () {
109
+ final FlutterSdk sdk = FlutterSdk .getFlutterSdk (myProject );
110
+ if (sdk != null ) {
111
+ previousSdkVersion = sdk .getVersion ();
112
+ }
98
113
mySdkCombo .getComboBox ().setEditable (true );
99
114
100
115
myCopyButton .setSize (ActionToolbar .DEFAULT_MINIMUM_BUTTON_SIZE );
@@ -264,13 +279,22 @@ public void apply() throws ConfigurationException {
264
279
265
280
final String sdkHomePath = getSdkPathText ();
266
281
if (FlutterSdkUtil .isFlutterSdkHome (sdkHomePath )) {
282
+
267
283
ApplicationManager .getApplication ().runWriteAction (() -> {
268
284
FlutterSdkUtil .setFlutterSdkPath (myProject , sdkHomePath );
269
285
FlutterSdkUtil .enableDartSdk (myProject );
286
+
270
287
ApplicationManager .getApplication ().executeOnPooledThread (() -> {
271
288
final FlutterSdk sdk = FlutterSdk .forPath (sdkHomePath );
272
289
if (sdk != null ) {
273
- sdk .queryFlutterChannel (false );
290
+ try {
291
+ lock .acquire ();
292
+ sdk .queryFlutterChannel (false );
293
+ lock .release ();
294
+ }
295
+ catch (InterruptedException e ) {
296
+ // do nothing
297
+ }
274
298
}
275
299
});
276
300
});
@@ -320,6 +344,27 @@ public void reset() {
320
344
}
321
345
322
346
onVersionChanged ();
347
+ if (sdk != null ) {
348
+ if (previousSdkVersion != null ) {
349
+ if (previousSdkVersion .compareTo (sdk .getVersion ()) != 0 ) {
350
+ final List <PubRoot > roots = PubRoots .forProject (myProject );
351
+ try {
352
+ lock .acquire ();
353
+ for (PubRoot root : roots ) {
354
+ sdk .startPubGet (root , myProject );
355
+ }
356
+ lock .release ();
357
+ }
358
+ catch (InterruptedException e ) {
359
+ // do nothing
360
+ }
361
+ previousSdkVersion = sdk .getVersion ();
362
+ }
363
+ }
364
+ }
365
+ else {
366
+ previousSdkVersion = null ;
367
+ }
323
368
324
369
myReportUsageInformationCheckBox .setSelected (FlutterInitializer .getCanReportAnalytics ());
325
370
@@ -375,17 +420,40 @@ private void onVersionChanged() {
375
420
return ;
376
421
}
377
422
423
+ // Moved launching the version updater to a background thread to avoid deadlock
424
+ // when the semaphone was locked for a long time on the EDT.
378
425
final ModalityState modalityState = ModalityState .current ();
379
-
380
- // TODO(devoncarew): Switch this to expecting json output.
381
- sdk .flutterVersion ().start ((ProcessOutput output ) -> {
382
- final String fullVersionText = output .getStdout ();
383
- fullVersionString = fullVersionText ;
384
-
385
- final String [] lines = StringUtil .splitByLines (fullVersionText );
386
- final String singleLineVersion = lines .length > 0 ? lines [0 ] : "" ;
387
- ApplicationManager .getApplication ().invokeLater (() -> updateVersionTextIfCurrent (sdk , singleLineVersion ), modalityState );
388
- }, null );
426
+ ApplicationManager .getApplication ().executeOnPooledThread (() -> {
427
+ try {
428
+ if (updater != null ) {
429
+ // If we get back here before the previous one finished then just kill it.
430
+ // This isn't perfect, but does help avoid printing this message most times:
431
+ // Waiting for another flutter command to release the startup lock...
432
+ updater .destroy ();
433
+ lock .release ();
434
+ }
435
+ Thread .sleep (100L );
436
+ lock .acquire ();
437
+
438
+ ApplicationManager .getApplication ().invokeLater (() -> {
439
+ // "flutter --version" can take a long time on a slow network.
440
+ updater = sdk .flutterVersion ().start ((ProcessOutput output ) -> {
441
+ fullVersionString = output .getStdout ();
442
+ final String [] lines = StringUtil .splitByLines (fullVersionString );
443
+ final String singleLineVersion = lines .length > 0 ? lines [0 ] : "" ;
444
+
445
+ ApplicationManager .getApplication ().invokeLater (() -> {
446
+ updater = null ;
447
+ lock .release ();
448
+ updateVersionTextIfCurrent (sdk , singleLineVersion );
449
+ }, modalityState );
450
+ }, null );
451
+ }, modalityState );
452
+ }
453
+ catch (InterruptedException e ) {
454
+ // do nothing
455
+ }
456
+ });
389
457
}
390
458
391
459
/***
0 commit comments