Friday, August 30, 2013

Dart's --package-root


I finally pushed my “real fake” Dart testing server, affectionately know as plummbur-kruk, out to GitHub and Dart Pub. It is in good enough shape that I think it is ready for use to support many of the browser code samples in Dart for Hipsters and in Hipster MVC (the library from the book).

I have published many a package to the http://pub.dartlang.org/, the primary package server for Dart, so that all went smoothly. I wrote up a nice README for the packages, mostly based on usage in Hipster MVC as I replaced previous testing solutions with plummbur-kruk. And it all went smoothly… until I tried to actually use it.

The pub package tool is pretty brilliant. I got Hipster MVC working with plummbur-kruk with a local path dependency in the pubspec.yaml file:
name: hipster_mvc
version: 0.2.7
description: Dart-based MVC framework
author: Chris Strom <chris@eeecomputes.com>
homepage: https://github.com/eee-c/hipster-mvc
dependencies:
  unittest: any
  browser: any
  plummbur_kruk:
    path: /home/chris/repos/plummbur-kruk
To switch to the version that I uploaded to pub.dartlang.org, I need only remove the path dependency:
name: hipster_mvc
# ...
dependencies:
  unittest: any
  browser: any
  plummbur_kruk: any
Then pub install. That went fine. What did not work was running the Bash scripts that I included last night. I kept getting the following error:
➜  hipster-mvc git:(http-test-server) ✗ ./test/run.sh
starting test server
content_shell --dump-render-tree test/index.html
Unable to open file: /home/chris/repos/hipster-mvc/packages/plummbur_kruk/packages/dirty/dirty.dart'file:///home/chris/repos/hipster-mvc/packages/plummbur_kruk/server.dart': Error: line 6 pos 1: library handler failed
import 'package:dirty/dirty.dart';
^
Bizarre! All of the sudden it cannot one of its own dependencies (dart dirty).

And indeed, even if I try to run the same Dart script directly, I get the same error:
➜  hipster-mvc git:(http-test-server) ✗ dart packages/plummbur_kruk/server.dart 
Unable to open file: /home/chris/repos/hipster-mvc/packages/plummbur_kruk/packages/dirty/dirty.dart'file:///home/chris/repos/hipster-mvc/packages/plummbur_kruk/server.dart': Error: line 6 pos 1: library handler failed
import 'package:dirty/dirty.dart';
^
What gives?

Well, this turns out to be one of the hazards of local development. I had manually created a symbolic link in the lib directory of my local development copy of plummbur-kruk. If I remove that and switch Hipster MVC back to use the local path dependency, then I get the same error.

The symbolic link is, in fact, invalid—the lib top-level directory in a Dart package should not link to the packages directory. As the name suggests, the packages directory contains all of the packages on which a Dart application or packages depends. It also includes a link to itself:
➜  plummbur-kruk git:(master) ✗ ls -l packages 
total 40
lrwxrwxrwx 1 chris chris 65 Aug 30 22:37 browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.19/lib
lrwxrwxrwx 1 chris chris 64 Aug 30 22:37 crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.19/lib
lrwxrwxrwx 1 chris chris 62 Aug 30 22:37 dirty -> /home/chris/.pub-cache/hosted/pub.dartlang.org/dirty-0.1.0/lib
lrwxrwxrwx 1 chris chris 62 Aug 30 22:37 http -> /home/chris/.pub-cache/hosted/pub.dartlang.org/http-0.6.19/lib
lrwxrwxrwx 1 chris chris 62 Aug 30 22:37 meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.19/lib
lrwxrwxrwx 1 chris chris 62 Aug 30 22:37 path -> /home/chris/.pub-cache/hosted/pub.dartlang.org/path-0.6.19/lib
lrwxrwxrwx 1 chris chris  6 Aug 30 22:37 plummbur_kruk -> ../lib
lrwxrwxrwx 1 chris chris 72 Aug 30 22:37 scheduled_test -> /home/chris/.pub-cache/hosted/pub.dartlang.org/scheduled_test-0.6.19/lib
lrwxrwxrwx 1 chris chris 69 Aug 30 22:37 stack_trace -> /home/chris/.pub-cache/hosted/pub.dartlang.org/stack_trace-0.6.19/lib
lrwxrwxrwx 1 chris chris 66 Aug 30 22:37 unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.19/lib
lrwxrwxrwx 1 chris chris 61 Aug 30 22:37 uuid -> /home/chris/.pub-cache/hosted/pub.dartlang.org/uuid-0.1.6/lib
Each package link points the lib top-level directory. So linking to the lib directory from the lib directory would create a very undesirable recursive link.

So am I out of luck in trying to get a Dart script to run directly from a package's lib directory? Nope. Enter the --package-root option for the dart VM. From the documentation, this option is:
Where to find packages, that is, "package:..." imports.
I still have a hard time remembering whether it is the “packages" directory itself or is supposed to be a directory that contains a “packages” directory. It is the former:
➜  hipster-mvc git:(http-test-server) ✗ dart --package-root=. packages/plummbur_kruk/server.dart
Unable to open file: /home/chris/repos/hipster-mvc/dirty/dirty.dart'file:///home/chris/repos/hipster-mvc/packages/plummbur_kruk/server.dart': Error: line 6 pos 1: library handler failed
import 'package:dirty/dirty.dart';
^
➜  hipster-mvc git:(http-test-server) ✗ dart --package-root=./packages packages/plummbur_kruk/server.dart
Server started on port: 31337
After fixing that back in the start.sh script of plummbur-kruk and publishing version 0.0.2 to pub.dartlang.org, I pub install the newest kruk:
➜  hipster-mvc git:(http-test-server) ✗ pub install
Resolving dependencies............
Downloading plummbur_kruk 0.0.2 from hosted...
Dependencies installed!
And now I have my Hipster MVC tests passing against a real fake server:
➜  hipster-mvc git:(http-test-server) ✗ ./test/run.sh
starting test server
content_shell --dump-render-tree test/index.html
Server started on port: 31337
...
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: unsupported remove
CONSOLE MESSAGE: PASS: Hipster Sync can parse regular JSON
CONSOLE MESSAGE: PASS: Hipster Sync can parse empty responses
CONSOLE MESSAGE: PASS: Hipster Sync HTTP get it can parse responses
CONSOLE MESSAGE: PASS: Hipster Sync HTTP post it can POST new records
CONSOLE MESSAGE: PASS: Hipster Sync (w/ a pre-existing record) HTTP PUT: can update existing records
CONSOLE MESSAGE: PASS: Hipster Sync (w/ a pre-existing record) HTTP DELETE: can remove the record from the store
CONSOLE MESSAGE: PASS: Hipster Sync (w/ multiple pre-existing records) can retrieve a collection of records
CONSOLE MESSAGE:
CONSOLE MESSAGE: All 8 tests passed.
I do not often have to use --package-root (and I almost almost always choose the wrong directory the first time). But when I have need of it, it does come in handy.


Day #859

No comments:

Post a Comment