(paths: string[], executableFilename: string, postFilter?: (s: string) => boolean)
| 650 | } |
| 651 | |
| 652 | public searchPaths(paths: string[], executableFilename: string, postFilter?: (s: string) => boolean): SdkSearchResults { |
| 653 | this.logger.info(`Searching for ${executableFilename}`); |
| 654 | |
| 655 | const rawSdkPaths = |
| 656 | paths |
| 657 | .filter((p) => p) |
| 658 | .map(resolvePaths) |
| 659 | .filter(notUndefined); |
| 660 | |
| 661 | // Any that don't end with bin, add it on (as an extra path) since some of our |
| 662 | // paths may come from places that don't already include it (for ex. the |
| 663 | // user config.sdkPath). |
| 664 | const isBinFolder = (f: string) => ["bin", "sbin"].includes(path.basename(f)); |
| 665 | let sdkPaths = flatMap( |
| 666 | rawSdkPaths, |
| 667 | (p): SdkSearchResult[] => isBinFolder(p) |
| 668 | ? [{ originalPath: p, sdkPath: p }] |
| 669 | : [{ originalPath: p, sdkPath: p }, { originalPath: p, sdkPath: path.join(p, "bin") }], |
| 670 | ); |
| 671 | |
| 672 | // TODO: Make the list unique, but preserve the order of the first occurrences. We currently |
| 673 | // have uniq() and unique(), so also consolidate them. |
| 674 | |
| 675 | this.logger.info(` Looking for ${executableFilename} in:`); |
| 676 | for (const p of sdkPaths) |
| 677 | this.logger.info(` ${this.sdkDisplayString(p)}`); |
| 678 | |
| 679 | // Restrict only to the paths that have the executable. |
| 680 | sdkPaths = sdkPaths.filter((p) => fs.existsSync(path.join(p.sdkPath, executableFilename))); |
| 681 | |
| 682 | this.logger.info(` Found at:`); |
| 683 | for (const p of sdkPaths) |
| 684 | this.logger.info(` ${this.sdkDisplayString(p)}`); |
| 685 | |
| 686 | // Keep track if we find something that looks like a package manager init script. These are |
| 687 | // symlinks like `flutter` that resolve to other binaries (like `snap` or `hermit`) and may need |
| 688 | // to be executed if we don't find a real SDK. |
| 689 | let sdkInitScript: string | undefined; |
| 690 | |
| 691 | // Convert all the paths to their resolved locations. |
| 692 | sdkPaths = sdkPaths.map((sdkPath): SdkSearchResult => { |
| 693 | // In order to handle symlinks on the binary (not folder), we need to add the executableName before calling realpath. |
| 694 | const fullPath = path.join(sdkPath.sdkPath, executableFilename); |
| 695 | const realExecutableLocation = safeRealpathSync(fullPath); |
| 696 | |
| 697 | if (realExecutableLocation.toLowerCase() !== fullPath.toLowerCase()) |
| 698 | this.logger.info(`Following symlink: ${fullPath} -> ${realExecutableLocation}`); |
| 699 | |
| 700 | // If the symlink resolves to a package manager binary, it's not a real SDK |
| 701 | // and we should return as-is rather than walk up two levels, as we |
| 702 | // may want to use the presence of this to trigger initialisation. |
| 703 | const targetBaseName = path.basename(realExecutableLocation); |
| 704 | if (targetBaseName !== executableFilename) { |
| 705 | this.logger.info(`Target ${targetBaseName} is not ${executableFilename}, assuming ${fullPath} is a package manager init script`); |
| 706 | sdkInitScript = fullPath; |
| 707 | return { originalPath: sdkPath.originalPath, sdkPath: fullPath }; |
| 708 | } |
| 709 |
no test coverage detected