anchor.diff:  
     PubTools pub = new PubTools();
     pub.install(context);
  -}
  -
  -void clean(GrinderContext context) {
  -  // delete the sdk directory
  -  runCommandSync(context, 'rm -rf app/sdk/lib');
  -  runCommandSync(context, 'rm app/sdk/version');
  -
  -  // delete any compiled js output
  -  runCommandSync(context, 'rm app/*.dart.js');
  -  runCommandSync(context, 'rm app/*.dart.precompiled.js');
  -  runCommandSync(context, 'rm app/*.js.map');
  -  runCommandSync(context, 'rm app/*.js.deps');
  -
  -  // TODO: delete the build/ dir?
  -
  -}
  -
  -void packages(GrinderContext context) {
  +
  +  _populateSdk(context);
  +
     // copy from ./packages to ./app/packages
     copyDirectory(
         joinDir(Directory.current, ['packages']),
         joinDir(Directory.current, ['app', 'packages']));
  -}
  -
  -// Prepare the build folder.
  -// It will copy all the required source files to build/.
  -// It will prepare the directory layout to be compatible with polymer builder.
  -void prepareBuild(GrinderContext context) {
  -  context.log('prepare build');
  -  // Copy files to build directory.
  -  copyFile(
  -      joinFile(Directory.current, ['pubspec.yaml']),
  -      joinDir(Directory.current, ['build']));
  -  copyFile(
  -      joinFile(Directory.current, ['pubspec.lock']),
  -      joinDir(Directory.current, ['build']));
  -  // Change the name of the folder to web because polymer builder will only
  -  // build content from 'web' folder.
  -  copyDirectory(
  -      joinDir(Directory.current, ['app']),
  -      joinDir(Directory.current, ['build', 'web']));
  -  copyDirectory(
  -      joinDir(Directory.current, ['packages']),
  -      joinDir(Directory.current, ['build', 'packages']));
  -  runCommandSync(context, 'rm -rf build/web/packages');
  -}
  -
  -// It will output a file web/spark.html_bootstrap.dart and a spark.html
  -// without HTML imports.
  -Future<bool> asyncPolymerBuild(GrinderContext context,
  -                               String entryPoint,
  -                               String outputDir) {
  -  var args = ['--out', outputDir, '--deploy'];
  -  var options = polymer.parseOptions(args);
  -  return polymer.build(entryPoints: [entryPoint], options: options).then((_) {
  -    context.log("polymer build done");
  -    return true;
  -  });
  -}
  -
  -// Transpile dart sources to JS.
  -// It will create spark.html_bootstrap.dart.precompiled.js.
  -void dart2JSBuild(GrinderContext context) {
  -  // We remove the symlink and replace it with a copy.
  -  runCommandSync(context, 'rm -rf web/packages');
  -  copyDirectory(
  -      joinDir(Directory.current, ['packages']),
  -      joinDir(Directory.current, ['web', 'packages']));
  -
  -  // We tell dart2js to compile to spark.dart.js; it also outputs a CSP
  -  // version (spark.dart.precompiled.js), which is what we actually use.
  -  runProcess(context, 'dart2js', arguments: [
  -      'web/spark.html_bootstrap.dart',
  -      '--out=web/spark.html_bootstrap.dart.js']);
  -
  -  // Support for old version of dart2js: they generate precompiled.js file.
  -  if (new File('web/precompiled.js').existsSync()) {
  -    new File('web/precompiled.js').renameSync(
  -        'web/spark.html_bootstrap.dart.precompiled.js');
  -  }
  -
  -  printSize(context,  new File('web/spark.html_bootstrap.dart.precompiled.js'));
  -}
  -
  -Future compile(GrinderContext context) {
  -  prepareBuild(context);
  -
  -  Directory.current = 'build';
  -  return Future.wait([asyncPolymerBuild(context, 'web/spark.html',
  -                                        'polymer-build').then((bool success) {
  -    Directory.current = 'polymer-build';
  -    dart2JSBuild(context);
  -    context.log('result has been written to build/polymer-build/web/');
  -    Directory.current = '../..';
  -  })]);
  -}
  -
  -void analyze(GrinderContext context) {
  -  runProcess(context, 'dartanalyzer', arguments: ['app/spark.dart']);
  -  runProcess(context, 'dartanalyzer', arguments: ['app/spark_test.dart']);
  -}
  -
  -void changeMode(GrinderContext context, bool useTestMode) {
  -  final testMode = 'src="spark_test.dart';
  -  final noTestMode = 'src="spark.dart';
  -
  -  File htmlFile = joinFile(Directory.current, ['app', 'spark.html']);
  -
  -  String contents = htmlFile.readAsStringSync();
  -
  -  if (useTestMode) {
  -    if (contents.contains(noTestMode)) {
  -      contents = contents.replaceAll(noTestMode, testMode);
  -      htmlFile.writeAsStringSync(contents);
  -    }
  -  } else {
  -    if (contents.contains(testMode)) {
  -      contents = contents.replaceAll(testMode, noTestMode);
  -      htmlFile.writeAsStringSync(contents);
  -    }
  -  }
  -}
  -
  -// Creates an archive of the Chrome App.
  -// - Sources will be compiled in Javascript using "compile" task
  -//
  -// We'll create an archive using the content of build-chrome-app.
  -// - Copy the compiled sources to build/chrome-app/spark
  -// - We clean all packages/ folders that have been duplicated into every
  -//   folders by the "compile" task
  -// - Copy the packages/ directory in build/chrome-app/spark/packages
  -// - Remove test
  -// - Zip the content of build/chrome-app-spark to dist/spark.zip
  -//
  -void archive(GrinderContext context) {
  -  Directory distDir = new Directory('dist');
  -  distDir.createSync();
  -
  -  // Create a build/chrome-app/spark directory to prepare the content of the
  -  // Chrome App.
  -  copyDirectory(
  -      joinDir(Directory.current, ['build', 'polymer-build', 'web']),
  -      joinDir(Directory.current, ['build', 'chrome-app', 'spark']));
  -  runCommandSync(
  -      context,
  -      'find build/chrome-app/spark -name "packages" -print0 | xargs -0 rm -rf');
  -  copyDirectory(
  -      joinDir(Directory.current, ['build', 'polymer-build', 'web', 'packages']),
  -      joinDir(Directory.current, ['build', 'chrome-app', 'spark', 'packages']));
  -  runCommandSync(context, 'rm -rf build/chrome-app/spark/test');
  -  runCommandSync(context, 'rm -rf build/chrome-app/spark/spark_test.dart');
  -
  -  // zip spark.zip . -r -q -x .*
  -  Directory.current = 'build/chrome-app/spark';
  -  runCommandSync(context, 'zip ../../../dist/spark.zip . -qr -x .*');
  -  Directory.current = '../../..';
  -  printSize(context, new File('dist/spark.zip'));
  -}
  -
  -void printSize(GrinderContext context, File file) {
  -  int sizeKb = file.lengthSync() ~/ 1024;
  -  context.log('${file.path} is ${_NF.format(sizeKb)}k');
  -}
  -
  -// Returns the name of the current branch.
  -String getBranchName() {
  -  return getCommandOutput('git branch | grep "*" | sed -e "s/\* //g"');
  -}
  -
  -// Returns the URL of the git repository.
  -String getRepositoryUrl() {
  -  return getCommandOutput('git config remote.origin.url');
  -}
  -
  -// Returns the current revision identifier of the local copy.
  -String getCurrentRevision() {
  -  return getCommandOutput('git rev-parse HEAD | cut -c1-10');
  -}
  -
  -// We can build a real release only if the repository is the original
  -// repository of spark and master is the working branch since we need to
  -// increase the version and commit it to the repository.
  -bool canReleaseFromHere() {
  -  return (getRepositoryUrl() == 'https://github.com/dart-lang/spark.git') &&
  -         (getBranchName() == 'master');
  -}
  -
  -// In case, release is performed on a non-releasable branch/repository, we just
  -// archive and name the archive with the revision identifier.
  -void archiveWithRevision(GrinderContext context) {
  -  context.log('Performing archive instead.');
  -  archive(context);
  -  File file = new File('dist/spark.zip');
  -  String version = getCurrentRevision();
  -  String filename = 'spark-rev-${version}.zip';
  -  file.rename('dist/${filename}');
  -  context.log("Created ${filename}");
  -}
  -
  -// Increase the build number in the manifest.json file. Returns the full
  -// version.
  -String increaseBuildNumber(GrinderContext context) {
  -  // Tweaking build version in manifest.
  -  File file = new File('app/manifest.json');
  -  String content = file.readAsStringSync();
  -  var manifestDict = JSON.decode(content);
  -  String version = manifestDict['version'];
  -  RegExp exp = new RegExp(r"(\d+\.\d+)\.(\d+)");
  -  Iterable<Match> matches = exp.allMatches(version);
  -  assert(matches.length > 0);
  -
  -  Match m = matches.first;
  -  String majorVersion = m.group(1);
  -  int buildVersion = int.parse(m.group(2));
  -  buildVersion ++;
  -
  -  version = '${majorVersion}.${buildVersion}';
  -  manifestDict['version'] = version;
  -  file.writeAsStringSync(JSON.encode(manifestDict));
  -
  -  // It needs to be copied to compile result directory.
  -  copyFile(
  -      joinFile(Directory.current, ['app', 'manifest.json']),
  -      joinDir(Directory.current, ['build', 'polymer-build', 'web']));
  -  return version;
  +
  +  BUILD_DIR.createSync();
  +  DIST_DIR.createSync();
  +}
  +
  +/**
  + * Copy all source to `build/deploy`. Do a polymer deploy to `build/deploy-out`.
  + * This builds the regular (non-test) version of the app.
  + */
  +void deploy(GrinderContext context) {
  +  Directory sourceDir = joinDir(BUILD_DIR, ['deploy']);
  +  Directory destDir = joinDir(BUILD_DIR, ['deploy-out']);
  +
  +  _polymerDeploy(context, sourceDir, destDir);
  +
  +  _dart2jsCompile(context,
  +      joinDir(destDir, ['web']), 'spark.html_bootstrap.dart');
  +}
  +
  +/**
  + * Copy all source to `build/deploy-test`. Do a polymer deploy to
  + * `build/deploy-test-out`. This builds a test version of the app.
  + */
  +void deployTest(GrinderContext context) {
  +  Directory sourceDir = joinDir(BUILD_DIR, ['deploy-test']);
  +  Directory destDir = joinDir(BUILD_DIR, ['deploy-test-out']);
  +
  +  _polymerDeploy(context, sourceDir, destDir);
  +
  +  _dart2jsCompile(context,
  +      joinDir(destDir, ['web']), 'spark.html_bootstrap.dart');
   }
   
   // Creates a release build to be uploaded to Chrome Web Store.
  
positive.code:  * Recursively create `.files` json files in the given directory; these files
   * serve as directory listings.
   */
  void createDirectoryListings(Directory dir) {
    List<String> files = [];
  
    String parentName = fileName(dir);
  
negative.code:   FileSet srcVer = new FileSet.fromFile(srcVersionFile);
    FileSet destVer = new FileSet.fromFile(destVersionFile);
  
    // check the state of the sdk/version file, to see if things are up-to-date
    if (!destVer.upToDate(srcVer)) {
      // copy files over
      context.log('copying SDK');
      copyFile(srcVersionFile, destSdkDir);
      copyDirectory(joinDir(srcSdkDir, ['lib']), joinDir(destSdkDir, ['lib']));
  
      // lib/_internal/compiler, dartdoc, and pub are not sdk libraries, but do
      // take up a lot of space; remove them
      _runCommandSync(context, 'rm -rf app/sdk/lib/_internal/compiler');
      _runCommandSync(context, 'rm -rf app/sdk/lib/_internal/dartdoc');
      _runCommandSync(context, 'rm -rf app/sdk/lib/_internal/pub');
  
      // traverse directories, creating a .files json directory listing
      context.log('creating SDK directory listings');
      _createDirectoryListings(destSdkDir);
    }
  }
  
  