Dart-Code / Dart-Code

Dart and Flutter support for VS Code
https://dartcode.org/
MIT License
1.47k stars 298 forks source link

"Could not find Dart in your Flutter SDK" message when PATH contains a bin/flutter wrapper script before the real SDK #5174

Closed Dieterbe closed 1 day ago

Dieterbe commented 4 days ago

I get this notification when starting vscode. Yet, flutter doctor shows everything is OK, and the "show log" button also shows a logfile which shows that actually, it can find dart. full log output here: https://gist.github.com/Dieterbe/8dc655aa221d497726018543e8d0a477

Could not find Dart in your Flutter SDK. Please run 'flutter doctor' in the terminal then reload the project once all issues are resolved.

image

the flutter doctor output is this:

[✓] Flutter (Channel stable, 3.22.2, on Arch Linux 6.9.7-arch1-1, locale en_US.utf-8)
    • Flutter version 3.22.2 on channel stable at /opt/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 761747bfc5 (4 weeks ago), 2024-06-05 22:15:13 +0200
    • Engine revision edd8546116
    • Dart version 3.4.3
    • DevTools version 2.34.3

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /home/dieter/Android/Sdk
    • Platform android-34, build-tools 33.0.0
    • Java binary at: /opt/android-studio/jbr/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • CHROME_EXECUTABLE = /usr/bin/google-chrome-stable

[✓] Linux toolchain - develop for Linux desktop
    • clang version 17.0.6
    • cmake version 3.29.6
    • ninja version 1.12.1
    • pkg-config version 2.1.1

[✓] Android Studio (version 2022.3)
    • Android Studio at /opt/android-studio
    • Flutter plugin version 76.3.2
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231)

[✓] Connected device (3 available)
    • Pixel 6 (mobile) • 1A111FDF6007TT • android-arm64  • Android 14 (API 34)
    • Linux (desktop)  • linux          • linux-x64      • Arch Linux 6.9.7-arch1-1
    • Chrome (web)     • chrome         • web-javascript • Google Chrome 126.0.6478.126

[✓] Network resources
    • All expected network resources are available.

• No issues found!
exit code 0

Screenshots If applicable, add screenshots to help explain your problem.

Please complete the following information:

You can run the Dart: Collect Diagnostic Information command from the VS Code command palette (F1) to easily capture this information or provide it manually.

DanTup commented 4 days ago

also shows a logfile which shows that actually, it can find dart

The message says that it can't find Dart in the Flutter SDK. For Flutter, we will only use a Dart SDK that is bundled with Flutter.

The issue seems to be that we're not detecting the correct location of a Flutter SDK:

[4:07:06 PM] [General] [Info]     Found at:
[4:07:06 PM] [General] [Info]         /usr/bin
[4:07:06 PM] [General] [Info]         /sbin
[4:07:06 PM] [General] [Info] Following symlink: /sbin/flutter -> /usr/bin/flutter
[4:07:06 PM] [General] [Info]     Candidate paths to be post-filtered:
[4:07:06 PM] [General] [Info]         /usr/bin -> /usr
[4:07:06 PM] [General] [Info]         /sbin -> /usr
[4:07:06 PM] [General] [Info]     Found at /usr/bin -> /usr
[4:07:06 PM] [General] [Info]     Returning SDK path /usr for flutter
[4:07:06 PM] [General] [Info] Flutter is not initialized, running 'flutter doctor' to force...

We found a Flutter binary at /usr/bin/flutter and because it ends with bin/flutter, we've concluded that /usr is the location of the Flutter SDK which is not correct, and therefore we've looked for a Dart SDK at /usb/bin/cache/dart-sdk/.

If we had discounted /usr as a Flutter SDK, probably you'd get a better error that we failed to find the Flutter SDK and you'd be offered to manually select it.

Can you confirm:

  1. Where does your Flutter SDK actually live on disk? (not just a flutter binary, but the entire git repo)
  2. What is the file at /usb/bin/flutter? Is it a symlink or a binary? If it's a binary, where did it come from/what is it? (the standard flutter binary needs to live inside its Git repository because it will read other files alongside it).

Thanks!

Dieterbe commented 3 days ago

Hello @DanTup thanks for your response. i have a full clone of ssh://git@github.com/flutter/flutter.git in /opt/flutter . (as setup by the flutter-bin AUR package on arch linux). this is my flutter SDK indeed. i don't think i have other flutter sdk's elsewhere, though i did install 'melos' recently but i don't think it's related.

for the 2nd question. it seems it is a wrapper script that was installed by the same flutter-bin package.

[root@xps17 ~]# cat /usr/bin/flutter
#!/usr/bin/env bash

source /usr/bin/flutter_init

if ! grep -q '/usr/bin' <<< "$(which flutter)"; then
  exec flutter "$@"
fi
[root@xps17 ~]# pacman -Qo /usr/bin/flutter
/usr/bin/flutter is owned by flutter-bin 3.22.2-1
[root@xps17 ~]# 

for completeness, i can add:

DanTup commented 3 days ago

Great, thanks for the info. I think I can fix this by expanding the check for what we consider a Flutter SDK:

https://github.com/Dart-Code/Dart-Code/blob/058ff8699e955e3ac4d1871df8672a2a8b1b29a7/src/extension/sdk/utils.ts#L552-L554

Right now we assume if we find a flutter executable that's inside a bin sub-folder, it's an SDK, but that's too greedy here. We could also check for some other files we know a Flutter SDK would have (bin/internal/ or packages/flutter).

I'll work on a fix and push it to the pre-release channel next week for you to try out. Thanks!

DanTup commented 3 days ago

Oh btw, I'm assuming that you can run /opt/flutter/bin/flutter with no problem (eg. /opt/flutter/bin/flutter doctor and /opt/flutter/bin/flutter run)? Since once I've fixed this, that's what we'll end up using from the extension. We have to track a full SDK because we use other tools from inside it, so we will always run Flutter directly from the SDK folder and not some wrapper script that happens to exist on PATH.

Dieterbe commented 3 days ago

Hi @DanTup thank you for the followup. I don't quite fully understand your proposed solution, but it sounds like it will make it work, so i'm all for it.

yes, i can run a project using /opt/flutter/bin/flutter run. as for doctor, it now shows a (minor?) warning:

~ ❯❯❯ /opt/flutter/bin/flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel stable, 3.22.2, on Arch Linux 6.9.7-arch1-1, locale en_US.utf-8)
    ! Warning: `flutter` on your path resolves to /usr/bin/flutter, which is not inside your current Flutter SDK checkout at /opt/flutter. Consider adding /opt/flutter/bin to the front of your path.
    ! Warning: `dart` on your path resolves to /usr/bin/dart, which is not inside your current Flutter SDK checkout at /opt/flutter. Consider adding /opt/flutter/bin to the front of your path.
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio (version 2022.3)
[✓] Connected device (2 available)
[✓] Network resources

! Doctor found issues in 1 category.

BTW, in case you need my PATH:

~ ❯❯❯ echo $PATH
/home/dieter/bin:/usr/local/bin:/usr/local/sbin:/home/dieter/scripts:/home/dieter/code/vcsh:/home/dieter/code/snip:/home/dieter/code/dmenu-solarized:/home/dieter/go/bin:/home/dieter/code/git-scripts:/home/dieter/code/codemod/src:/home/dieter/code/dclean:/home/dieter/code/ddm:/home/dieter/code/svn-scripts:/usr/share/bcc/tools:/home/dieter/.cargo/bin:/home/dieter/code/aconfmgr:/home/dieter/.pub-cache/bin:/opt/google-cloud-cli/bin:/usr/bin:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/dieter/.local/bin:/sbin:/home/dieter/.npm-global/bin:/home/dieter/code/kubetail:/home/dieter/go/src/github.com/junegunn/fzf/bin
DanTup commented 3 days ago

I don't quite fully understand your proposed solution, but it sounds like it will make it work, so i'm all for it.

If you don't explicitly specify a Flutter SDK (such as using the dart.flutterSdkPath setting), we will try to find a Flutter SDK from your PATH. We do this by looking for a flutter binary, then if it's a symlink we resolve it, and then we look at the remaining path to see if it looks like a Flutter SDK (we can't just assume a binary named flutter is part of an SDK because in the past some users have had unrelated binaries named flutter on their PATH!).

The problem is out check for what "looks like a Flutter SDK" is fairly basic (if the binary is at /foo/bin/flutter we assume /foo is a Flutter SDK). The proposal is to expand the check to see if there are some other indications that this is a Flutter SDK (which your wrapper script would not pass). That means we'd skip over /usr/bin/flutter in your case and continue searching. If /opt/flutter/bin is in your PATH then we'd fine it, but if not, we would give you a better message saying we'd failed to find Flutter and give you a "Locate SDK" button to let you manually browse to the SDK folder (which we'd then write into the dart.flutterSdkPath setting).

That said, the details are probably not that important to you - I'll get a fix into the pre-release and you can test it out and if it still doesn't seem to work as expected, we can refine it :-)

Dieterbe commented 3 days ago

I suppose I could also try removing /usr/bin/flutter . The AUR package installs it but I wasn't sure why so I asked the maintainer. You can see their answer here https://aur.archlinux.org/packages/flutter-bin#comment-981177 . Seems like they are trying to achieve the opposite : steer people away from writing to /opt/flutter (via unionfs ?) If any of this feels wrong to you I'm prepared to get rid of the package and follow the manual flutter installation guide. I've used other packages in the past it never seems to quite fit the idea of the flutter developers (who want to install in a writeable dir) but packages are usually installed systemwide (outside of home) where regular users don't have write permissions (although some group permissions magic fixes that but it's not considered "pure")

DanTup commented 3 days ago

I suppose I could also try removing /usr/bin/flutter .

If you're just trying to get things working for you, you could also just put /opt/flutter in your PATH ahead of /usr, or you could add:

"dart.flutterSdkPath": "/opt/flutter",

...to your VS Code user settings. I think we should make Dart-Code handle this case better though, so we can automatically detect it for the benefit of other/future users :)

The AUR package installs it but I wasn't sure why so I asked the maintainer. You can see their answer here https://aur.archlinux.org/packages/flutter-bin#comment-981177 . Seems like they are trying to achieve the opposite : steer people away from writing to /opt/flutter (via unionfs ?)

I read that, but I'm afraid I don't entirely understand it (I'm not very familiar with how these things work on Linux). It's true that Flutter requires write access to its own folder, and I have seen some complaints about that from a number of package managers (because they really want to just install a read-only package and have any writing done elsewhere).

The simplest way to install Flutter IMO is to just git clone it somewhere and then add the bin folder to PATH. However, it's not clear to me from the thread above if there are other packages depending on a Flutter package, which probably complicates things a little (although it's not clear to me what depending on a Flutter package really means - software built with Flutter would presumably distribute compiled apps and not need a Flutter SDK?).

Dieterbe commented 2 days ago

If you're just trying to get things working for you, you could also just put /opt/flutter in your PATH ahead of /usr,

you mean /opt/flutter/bin ahead of /usr/bin, and indeed, this seems to work very well. doctor finds 0 issues and i can "flutter run" my apps just fine.

I read that, but I'm afraid I don't entirely understand it (I'm not very familiar with how these things work on Linux).

so basically this particular package creates a "union filesystem" which is a new special directory within our usere's (mine) home directory, that we use as our flutter SDK directory. behind the scenes, all reads "secretly" are served from the real sdk dir in /opt/flutter, and writes go to a special directory (also in our home folder) that only holds written files, not the full SDK dir. so this is kindof best of both worlds. packaging best practices are held up (system wide read only installation) while keeping flutter happy (allowing writes), at the cost of extra, arguably unnecessary complication..

However, it's not clear to me from the thread above if there are other packages depending on a Flutter package, which probably complicates things a little (although it's not clear to me what depending on a Flutter package really means - software built with Flutter would presumably distribute compiled apps and not need a Flutter SDK?).

this is the main reason i'm trying to use a formal package rather than just installing it myself in my home dir, because other flutter apps are built as source packages, so installing them depends on the flutter package to provide the SDK (regardless of what i may have done myself in my home directory) to build the software.

DanTup commented 2 days ago

all reads "secretly" are served from the real sdk dir in /opt/flutter, and writes go to a special directory (also in our home folder) that only holds written files, not the full SDK dir. so this is kindof best of both worlds. packaging best practices are held up (system wide read only installation) while keeping flutter happy (allowing writes), at the cost of extra, arguably unnecessary complication..

Ah, got it - that sounds reasonable. Although it's not clear to me why it needs the wrapper script for this - why can't /opt/flutter just be on path without a wrapper script?

because other flutter apps are built as source packages

Ah, I guess this makes some sense because it's much more portable.

I'll see what I can do to improve this. At worst, we'll prompt you to locate the SDK manually (instead of the confusing message we currently show), but if we can find /opt/flutter automatically, that would just avoid this altogether. If /opt/flutter is a common location for some linux distros, we could add that to the search list so even if it's not on PATH itself, we could find Flutter there when we don't find it anywhere else in the PATH.

Dieterbe commented 2 days ago

Although it's not clear to me why it needs the wrapper script for this - why can't /opt/flutter just be on path without a wrapper script?

The wrapper script is needed to set up the union filesystem "on-demand".

Looking forward to your tweak(s) :-)

Dieterbe commented 2 days ago

Note that flutter binaries or scripts (outside the sdk dir) can still exec() another flutter binary (inside the sdk dir). That's what's happening when using this package. It probably fails the check because it's not a symlink, yet in practice it works similarly

Your idea to look in /opt/flutter seems like a good one, even if this would bypass what the package is trying to do. That said, for my needs it will work because I gave myself write permissions to /opt/flutter but others may expect the unionfs wrapper to work. Not sure if from your perspective that's something you want to support. I have another idea that's maybe not so great but would solve this maybe more comprehensively: is there any config file the user could put either in the sdk dir or their home dir allowing us to vouch that a given bin/flutter really does call another flutter in the sdk dir, eg via fork/exec . (which I assume is not easily detectable with your scripts )

Dieterbe commented 2 days ago

I suppose what you probably ultimately really care about, is not so much that the flutter bin is inside the SDK dir per se, but that 1) whichever flutter bin/script is used, it matches the desired sdk version 2) whenever the sdk version is changed, the flutter bin/script is adjusted accordingly

You could check the first quite easily. And technically you could also validate the 2nd, but of course switching SDK versions automatically any time you want to do your check, sounds pretty bad. But maybe the first check is the one that matters most

DanTup commented 1 day ago

The wrapper script is needed to set up the union filesystem "on-demand".

Is this just something that needs to happen the first time? Is it fair for Dart-Code to just ignore this and assume it will always have happened? I'm not familiar with the install flow here but there are so many package managers that work differently, I really don't want to add special cases for them all (we actually already have some special casing to handle something very similar to this that was added for Snap... it was made generic, but Snap uses a symlink to a Snap binary instead of a wrapper script, so that support doesn't help here).

I think it's probably fair to say in the case where a user manages to set this up without the unionfs, they can just close VS Code, run flutter doctor from the terminal, and then everything should be fine (this is likely to be the first debugging step anyway).

is there any config file the user could put either in the sdk dir or their home dir allowing us to vouch that a given bin/flutter really does call another flutter in the sdk

This won't work because we're not looking for a flutter binary, we're looking for an SDK because we need to launch other tools from it too (for example we need to run {flutter-sdk}/bin/cache/dart-sdk/bin/dart language-server for the language server). We just use the binary to find the SDK, and then we always use absolute paths into that SDK to run all tools (this makes it easier to decouple the selected SDK from what's on PATH, because we support switching between SDKs within VS Code).

DanTup commented 1 day ago

I've pushed a new pre-release version of the Dart extension (v3.93.20240708) that will:

  1. Check for some additional files before determining something is a Flutter SDK
  2. Add /opt/flutter to a list of fallback places to search

Between these, I think they'll solve your issue (as long as /opt/flutter is writable with the unionfs.. otherwise you'd need to have run flutter from a terminal first.. we could investigate additional options for this if it turns out to be a common case).

It might take 10-15 mins for the pre-release to show up, you'll want to install it, remove /opt/flutter from the start of PATH, and then try creating a new Flutter project (Flutter: New Project) without having an existing Flutter project open (since if you have an open project, we will probably discover the SDK via its .dart_tool/package_config.json file).

You should be able to use Dart: Open Extension Log to verify the search paths (that /usr is not located as an SDK, and /opt/flutter is at the end of the search list).

Dart/Flutter pre-release versions