New Dart SDK helps eliminates symlinks

In which we retell the story of Dart and symlinks, investigate the new .packages file, and create a simpler world for Dart developers everywhere.

tldr: The Dart team is working towards a world where symlinks are no longer required because of the new .packages file.


Why a new solution for locating packages?

In the long long ago, during the before times (aka 2011), Dart could only run monolithic scripts and apps. In those early days, Dart didn't have support for packages/shared libraries. Then came package: URIs, which opened the door for sharing code. However, the platform only had one way to resolve that package: URI: look inside a packages/ directory next to the file importing the library. (Later, a --package-root option was added for more flexibility.) Thus began Dart's reliance on symlinks as a way of creating the packages/ directories next to all locations containing Dart scripts or libraries. Those symlinks and the package: scheme helped ushered in a boom of third-party libraries, the pub package manager, pub.dartlang.org, and more.

However, turns out that a reliance on symlinks leads to numerous issues, especially for our Windows developers. Putting symlinks into each subdirectory makes for a cluttered project. A directory full of symlinks only works on your local machine. The Windows operating system doesn't support symlinks (though, through some research, the pub authors did come up with a solution for modern Windows installs with something calls junction points). It became clear that the Dart platform needed a more robust and formal way to connect your script to your dependencies and third-party libraries.

The search began for a solution, and DEP5 (formally known as package spec) was born. The proposal specifies a new .packages file and a discovery workflow, and eliminates the need for symlinks everywhere. I am very happy to report that Dart SDK 1.12 will be the first version of Dart that doesn't require symlinks.

What is .packages?

The new .packages file is a simple map between a package name and a URI to the package's lib directory. Here is an example:

# This file has been generated by the Dart tool pub on Apr 14 09:14:14 2015.
# It contains a map from Dart package names to Dart package locations.
# Dart tools, including Dart VM and and Dart analyzer, rely on the content.
# AUTO GENERATED - DO NOT EDIT
unittest:/home/somebody/.pub/cache/unittest-0.9.9/lib/
async:/home/somebody/.pub/cache/async-1.1.0/lib/
quiver:/home/somebody/.pub/cache/quiver-1.2.1/lib/

The .packages file is auto-generated by pub, version 1.12-dev or greater.

Where does .packages live?

The .packages usually lives at the root of your package/app. For example:

my_app/
  README.md
  CHANGELOG.md
  .packages
  pubspec.yaml
  lib/
    my_lib.dart
  bin/
    my_app.dart

The Dart runtimes, such as the VM, dartjs, and analyzer, will traverse up directories, from the entry point script, to find a .packages. Thus, you do not need a .packages next to each script that you need to run or compile.

Do I check in .packages?

Do not check in .packages, because it contains paths that are local to your system. You can safely tell your source control system to ignore this file.

How do I use the file?

Under normal circumstances, you do not need to do anything special to use the file. pub creates the .packages file automatically.

The Analysis Server also uses .packages, so IDE/editor users (e.g. Atom, WebStorm, etc) should be able to work in a project without symlinks.

You can optionally specify a .packages as a command-line argument. For example:

dart --packages=path/to/a/.packages main.dart

Do I still need a packages/ directory in my project?

No. With .packages, there is no longer a need for symlinks or a packages/ directory in your project. Check out the next question for how to get to this beautiful new world.

Great! So, how can I eliminate symlinks?

You can use a hidden --no-package-symlinks command-line argument to instruct pub not to generate symlinks. For example:

pub get --no-package-symlinks

The above command will generate a .packages file in the root of your project, and will not generate any symlinks or packages/ directories.

Caveats

If you use WebStorm or IntelliJ, you may need to upgrade to WebStorm 11 EAP or the latest IntelliJ EAP to correctly work with .packages and no symlinks. The Dart plugin for JetBrains did add support for .packages, but it might not work with WebStorm 10. If you can't upgrade, do not run pub with --no-package-symlinks.

Next steps

Please install a 1.12-dev or later of the Dart SDK and test your code with the new .packages file. We eagerly await your feedback, especially from developers using Windows. You can also follow the meta-issue as we complete our implementation. Thanks!

Popular posts from this blog

Lists and arrays in Dart

Converting Array to List in Scala

Null-aware operators in Dart