`dart pub global activate` rebuilding unnecessarily
I've used dart pub global activate to make a tool usable directly from the command line. It works, but it's rebuilding the tool every time I invoke it. The tool (named qq) is just a thin git wrapper I wrote, customized to my workflow. It's unpublished, I just clone its git repo.
Here's an example output. It says Building package executable... Built qq:qq every time I invoke it:
My Dart SDK comes from Flutter. My Flutter SDK is from a cloned git repo:
liama@liama-macbookpro ~/d/n/p/ffigen (swiftgen2)> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel master, 3.23.0-13.0.pre.117, on macOS 14.5 23F79 darwin-x64, locale en)
! Upstream repository [email protected]:liamappelbe/flutter.git is not a standard remote.
Set environment variable "FLUTTER_GIT_URL" to [email protected]:liamappelbe/flutter.git to dismiss this error.
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[!] Xcode - develop for iOS and macOS (Xcode 15.0.1)
! CocoaPods 1.11.2 out of date (1.13.0 is recommended).
CocoaPods is a package manager for iOS or macOS platform code.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To update CocoaPods, see https://guides.cocoapods.org/using/getting-started.html#updating-cocoapods
[✓] Chrome - develop for the web
[✓] Android Studio (version 2020.3)
[✓] VS Code (version 1.91.0)
[✓] VS Code (version 1.74.2)
[✓] Connected device (2 available)
! Error: Browsing on the local area network for iPhone. Ensure the device is unlocked and attached with a cable or associated with the
same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources
! Doctor found issues in 2 categories.
Summary: The user is experiencing unnecessary rebuilds of a custom tool (qq) activated globally using dart pub global activate. The tool is a thin git wrapper and is not published, but rather cloned from a git repository. Every invocation of the tool triggers a rebuild, even though no changes have been made.
The bug seems to have gotten worse overnight. Today it's resolving dependencies twice before starting to rebuild the tool:
@liamappelbe feel free to share a minimally reproduciable example. Actually if you're activating from path I wouldn't be surprised if we throw away the precompilation everytime.
But double resolution of dependencies is weird :rofl:
Also include dart --version.
Minimal example:
dart create -t cli testcli
dart pub global activate --source path testcli
dart pub global run testcli
dart pub global run testcli # Rebuilds unnecessarily
Dart version: Dart SDK version: 3.6.0-78.0.dev (dev) (Wed Jul 24 13:03:02 2024 -0700) on "macos_x64"
When you are activating from path, the package is considered "mutable" so we rebuild the executable each time.
We should though be using incremental compilation, so the second build should be much faster - are you seeing that?
But double resolution of dependencies is weird 🤣
Yeah - that seems wrong.
I was however not able to reproduce this.
> dart create -t cli testcli
Creating testcli using template cli...
[...]
> dart pub global activate -spath testcli
Resolving dependencies in `testcli`...
Downloading packages...
Got dependencies in `testcli`!
Activated testcli 0.0.1 at path "/usr/local/google/home/sigurdm/projects/blah/testcli".
> dart pub global run testcli
Building package executable...
Built testcli:testcli.
Positional arguments: []
> dart pub global run testcli
Building package executable...
Built testcli:testcli.
Positional arguments: []
@liamappelbe can you give a simple reproduction of the reresolution of dependencies?
As a workaround for global activateing a local package and avoiding recompilation you can check your app into git, and run
dart pub global activate --source git testcli/
This will activate a static view of the testcli package (and thus not reflect local updates to the package without reactivation).
Also note that the "Building package executable... " output from pub is suppressed when no terminal is attached to the process.
When you are activating from path, the package is considered "mutable" so we rebuild the executable each time.
Could we add a flag to dart pub global activate to disable rebuilding for path activations? A flag to replicate the behaviour we get for a git activation?
We should though be using incremental compilation, so the second build should be much faster - are you seeing that?
The build is small enough that I'm not noticing any difference between the first and second runs. It's about 2 seconds each time to build and execute the script (this is true of both the testcli example, and my qq script).
If I write a bash script that just checks whether the snapshot exists, and runs it directly if it does, then the time reduces to 1 second (and I don't get spurious prints). But I think it'd be better if this behavior was simply a flag in the activate command.
@liamappelbe can you give a simple reproduction of the reresolution of dependencies?
Not really. It seems to just happen randomly on my Mac. The steps I outlined above are enough to reproduce it flakily on my Mac.
Stop treating developers like they’re incapable of making informed decisions. We know exactly what we’re doing when we activate from a path, and we don’t need this unnecessary hand-holding. The fact that future changes to the source won’t be reflected in the activated executable? Yeah, we can grasp that concept perfectly fine.
I want to be in full control of what's displayed on my screen. I don’t need the system cluttering it with unnecessary info or decisions I didn’t ask for. I actually prefer to manage things myself—like any normal developer would.
Add a flag to disable rebuilding, or better yet, don’t rebuild at all.
The fact that future changes to the source won’t be reflected in the activated executable? Yeah, we can grasp that concept perfectly fine.
Actually, I would expect changes I make in the source to be reflected in the activated executable. My primary workflow is to change the source and then run dart pub global activate -s path <path>. So I'm either dumb or your statement is over-reaching.
I want to be in full control of what's displayed on my screen...like any normal developer would.
Again, I consider myself a "normal" developer and while yes, I want to understand why something is printed, I would rather lean towards verbosity if it meant I had a better understanding of what was going on.
The road to hell is paved with good intentions.
How about the workflow of make, that does a timestamp comparison between the dependencies and only re-builds if the timestamps of the source files are greater than the generated binary?