11import * as fs from "fs" ;
2- import * as os from "os" ;
32import * as path from "path" ;
43import { commands , ExtensionContext , window } from "vscode" ;
5- import { analyzerSnapshotPath , dartPlatformName , dartVMPath , DART_DOWNLOAD_URL , executableNames , flutterPath , flutterSnapScript , FLUTTER_CREATE_PROJECT_TRIGGER_FILE , FLUTTER_DOWNLOAD_URL , initializeSnapPrompt , isLinux , isMac , isWin , showLogAction } from "../../shared/constants" ;
6- import { Logger , Sdks , WorkspaceConfig } from "../../shared/interfaces" ;
4+ import { analyzerSnapshotPath , dartPlatformName , dartVMPath , DART_DOWNLOAD_URL , executableNames , flutterPath , FLUTTER_CREATE_PROJECT_TRIGGER_FILE , FLUTTER_DOWNLOAD_URL , isLinux , isMac , isWin , showLogAction , snapBinaryPath , snapFlutterBinaryPath } from "../../shared/constants" ;
5+ import { Logger , Sdks , SdkSearchResults , WorkspaceConfig } from "../../shared/interfaces" ;
76import { nullLogger } from "../../shared/logging" ;
87import { PackageMap } from "../../shared/pub/package_map" ;
98import { flatMap , isDartSdkFromFlutter , notUndefined } from "../../shared/utils" ;
109import { fsPath , getSdkVersion , hasPubspec } from "../../shared/utils/fs" ;
1110import { resolvedPromise } from "../../shared/utils/promises" ;
12- import { processBazelWorkspace , processFlutterSnap , processFuchsiaWorkspace , processKnownGitRepositories } from "../../shared/utils/workspace" ;
11+ import { processBazelWorkspace , processFuchsiaWorkspace , processKnownGitRepositories } from "../../shared/utils/workspace" ;
1312import { envUtils , getAllProjectFolders , getDartWorkspaceFolders } from "../../shared/vscode/utils" ;
1413import { WorkspaceContext } from "../../shared/workspace" ;
1514import { Analytics } from "../analytics" ;
@@ -106,7 +105,7 @@ export class SdkUtils {
106105
107106 public async showSdkActivationFailure (
108107 sdkType : string ,
109- search : ( path : string [ ] ) => string | undefined ,
108+ search : ( path : string [ ] ) => Promise < SdkSearchResults > ,
110109 downloadUrl : string ,
111110 saveSdkPath : ( path : string ) => Thenable < void > ,
112111 commandToReRun ?: string ,
@@ -127,9 +126,9 @@ export class SdkUtils {
127126 const selectedFolders =
128127 await window . showOpenDialog ( { canSelectFolders : true , openLabel : `Set ${ sdkType } SDK folder` } ) ;
129128 if ( selectedFolders && selectedFolders . length > 0 ) {
130- const matchingSdkFolder = search ( selectedFolders . map ( fsPath ) ) ;
131- if ( matchingSdkFolder ) {
132- await saveSdkPath ( matchingSdkFolder ) ;
129+ const matchingSdkFolder = await search ( selectedFolders . map ( fsPath ) ) ;
130+ if ( matchingSdkFolder . sdkPath ) {
131+ await saveSdkPath ( matchingSdkFolder . sdkPath ) ;
133132 await promptToReloadExtension ( ) ;
134133 if ( commandToReRun ) {
135134 commands . executeCommand ( commandToReRun ) ;
@@ -237,7 +236,6 @@ export class SdkUtils {
237236 // TODO: Remove this lambda when the preview flag is removed.
238237 await processWorkspaceType ( findBazelWorkspaceRoot , ( l , c , b ) => processBazelWorkspace ( l , c , b , config . previewBazelWorkspaceCustomScripts ) ) ;
239238 const fuchsiaRoot = await processWorkspaceType ( findFuchsiaRoot , processFuchsiaWorkspace ) ;
240- await processWorkspaceType ( findFlutterSnapSdkRoot , processFlutterSnap ) ;
241239
242240 if ( fuchsiaRoot ) {
243241 this . logger . info ( `Found Fuchsia root at ${ fuchsiaRoot } ` ) ;
@@ -261,7 +259,20 @@ export class SdkUtils {
261259 "~/.flutter-sdk" ,
262260 ] . concat ( paths ) . filter ( notUndefined ) ;
263261
264- const flutterSdkPath = this . findFlutterSdk ( flutterSdkSearchPaths ) ;
262+ let flutterSdkResult = await this . findFlutterSdk ( flutterSdkSearchPaths ) ;
263+
264+ // Handle the case where the Flutter snap has not been initialised.
265+ if ( ! flutterSdkResult . sdkPath && flutterSdkResult . candidatePaths . includes ( snapBinaryPath ) ) {
266+ // Trigger initialization.
267+ this . logger . info ( `No Flutter SDK found, but a 'flutter' binary did point at ${ snapBinaryPath } so attempting to initialize...` ) ;
268+ await initializeFlutterSdk ( this . logger , snapFlutterBinaryPath ) ;
269+
270+ // Then search again.
271+ this . logger . info ( `Snap initialization completed, searching for Flutter SDK again...` ) ;
272+ flutterSdkResult = await this . findFlutterSdk ( flutterSdkSearchPaths ) ;
273+ }
274+
275+ const flutterSdkPath = flutterSdkResult . sdkPath ;
265276
266277 // Since we just blocked on a lot of sync FS, yield.
267278 await resolvedPromise ;
@@ -296,7 +307,8 @@ export class SdkUtils {
296307 // Since we just blocked on a lot of sync FS, yield.
297308 await resolvedPromise ;
298309
299- const dartSdkPath = this . findDartSdk ( dartSdkSearchPaths ) ;
310+ const dartSdkResult = await this . findDartSdk ( dartSdkSearchPaths ) ;
311+ const dartSdkPath = dartSdkResult . sdkPath ;
300312
301313 // Since we just blocked on a lot of sync FS, yield.
302314 await resolvedPromise ;
@@ -330,7 +342,7 @@ export class SdkUtils {
330342 return fs . existsSync ( fullPath ) && fs . statSync ( fullPath ) . isFile ( ) ;
331343 }
332344
333- public searchPaths ( paths : string [ ] , executableFilename : string , postFilter ?: ( s : string ) => boolean ) : string | undefined {
345+ public async searchPaths ( paths : string [ ] , executableFilename : string , postFilter ?: ( s : string ) => boolean ) : Promise < SdkSearchResults > {
334346 this . logger . info ( `Searching for ${ executableFilename } ` ) ;
335347
336348 let sdkPaths =
@@ -362,7 +374,7 @@ export class SdkUtils {
362374 this . logger . info ( ` ${ p } ` ) ;
363375
364376 // Convert all the paths to their resolved locations.
365- sdkPaths = sdkPaths . map ( ( p ) => {
377+ sdkPaths = await Promise . all ( sdkPaths . map ( async ( p ) => {
366378 const fullPath = path . join ( p , executableFilename ) ;
367379
368380 // In order to handle symlinks on the binary (not folder), we need to add the executableName before calling realpath.
@@ -371,9 +383,16 @@ export class SdkUtils {
371383 if ( realExecutableLocation . toLowerCase ( ) !== fullPath . toLowerCase ( ) )
372384 this . logger . info ( `Following symlink: ${ fullPath } ==> ${ realExecutableLocation } ` ) ;
373385
386+ // If the symlink resolves to the Snap binary, it's not a real SDK
387+ // and we should return as-is rather than walk up two levels, as we
388+ // may want to use the presence of this to trigger initialisation.
389+ if ( realExecutableLocation === snapBinaryPath ) {
390+ return snapBinaryPath ;
391+ }
392+
374393 // Then we need to take the executable name and /bin back off
375394 return path . dirname ( path . dirname ( realExecutableLocation ) ) ;
376- } ) ;
395+ } ) ) ;
377396
378397 // Now apply any post-filters.
379398 this . logger . info ( " Candidate paths to be post-filtered:" ) ;
@@ -384,9 +403,10 @@ export class SdkUtils {
384403 if ( sdkPath )
385404 this . logger . info ( ` Found at ${ sdkPath } ` ) ;
386405
406+
387407 this . logger . info ( ` Returning SDK path ${ sdkPath } for ${ executableFilename } ` ) ;
388408
389- return sdkPath ;
409+ return { sdkPath, candidatePaths : sdkPaths } ;
390410 }
391411}
392412
@@ -461,25 +481,6 @@ async function findGitRoot(logger: Logger, folder: string): Promise<string | und
461481 return findRootContaining ( folder , ".git" ) ;
462482}
463483
464- async function findFlutterSnapSdkRoot ( logger : Logger , folder : string ) : Promise < string | undefined > {
465- if ( isLinux && fs . existsSync ( flutterSnapScript ) ) {
466- logger . info ( `Found Flutter snap script` ) ;
467- const snapSdkRoot = path . join ( os . homedir ( ) , "/snap/flutter/common/flutter" ) ;
468-
469- if ( ! fs . existsSync ( snapSdkRoot + "/.git" ) ) {
470- logger . info ( `Flutter snap is not initialized, showing prompt` ) ;
471- await initializeFlutterSdk ( logger , flutterSnapScript , initializeSnapPrompt ) ;
472- }
473-
474- if ( fs . existsSync ( snapSdkRoot + "/.git" ) ) {
475- logger . info ( `Returning ${ snapSdkRoot } as Flutter snap SDK root` ) ;
476- return snapSdkRoot ;
477- }
478- }
479- return undefined ;
480- }
481-
482-
483484function findRootContaining ( folder : string , childName : string , expectFile = false ) : string | undefined {
484485 if ( folder ) {
485486 // Walk up the directories from the workspace root, and see if there
0 commit comments