docs: function resolve(options, callback) {
      if (typeof callback !== 'function') throw new Error('Second argument must be a callback');
      if (!options) return callback(new Error('options is required'));
      if (!options.mml) return callback(new Error('options.mml is required'));
      if (!options.base) return callback(new Error('options.base is required'));
      if (!options.cache) return callback(new Error('options.cache is required'));
      if (typeof options.nosymlink === "undefined") options.nosymlink = false;
      if (typeof options.benchmark === "undefined") options.benchmark = false;
      // respect global no-symlinking preference on windows
      if (never_link) options.nosymlink = true;
  
      var mml = options.mml,
          base = path.resolve(options.base),
          cache = path.resolve(options.cache),
          resolved = JSON.parse(JSON.stringify(mml)),
          nosymlink = options.nosymlink,
          benchmark = options.benchmark;
  
      Step(function setup() {
          if (nosymlink) mkdirp(base, 0755, this);
          else mkdirp(path.join(base, 'layers'), 0755, this);
      }, function style_externals(err) {
          if (err && err.code !== 'EEXIST') throw err;
          if (benchmark) console.time("[millstone][benchmark] Resolving style (mss) externals");
          var remaining = mml.Stylesheet.length;
          var error = null;
          var next = function(err) {
              remaining--;
              if (err && err.code !== 'EEXIST') error = err;
              if (remaining <= 0) this(error);
          }.bind(this);
  
          if (!remaining) return this();
  
          // Handle URIs within the Carto CSS
          function localizeCartoURIs(s,cb) {
              // Get all unique URIs in stylesheet
              // TODO - avoid finding non url( uris?
              var matches = s.data.match(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi);
              var URIs = _.uniq(matches || []);
              var CartoURIs = [];
              // remove any matched urls that are not clearly
              // part of a carto style
              // TODO - consider moving to carto so we can also avoid
              // downloading commented out code
              URIs.forEach(function(u) {
                  var idx = s.data.indexOf(u);
                  if (idx > -1) {
                      var pre_url = s.data.slice(idx-5,idx);
                      if (pre_url.indexOf('url(') > -1) {
                          CartoURIs.push(u);
                          // Update number of async calls so that we don't
                          // call this() too soon (before everything is done)
                          remaining += 1;
                      }
                  }
              });
  
              CartoURIs.forEach(function(u) {
                  var uri = url.parse(encodeURI(u));
                  // URL.
                  if (uri.protocol && (uri.protocol == 'http:' || uri.protocol == 'https:')) {
                      var filepath = path.join(cache, cachepath(u));
                      localize(uri.href, {filepath:filepath,name:s.id}, function(err, file) {
                          if (err) {
                              cb(err);
                          } else {
                              var extname = path.extname(file);
                              if (!extname) {
                                  readExtension(file, function(error, ext) {
                                      // note - we ignore any readExtension errors
                                      if (ext) {
                                          var new_filename = file + ext;
                                          fs.rename(file, new_filename, function(err) {
                                              s.data = s.data.split(u).join(new_filename);
                                              cb(err);
                                          });
                                      } else {
                                          s.data = s.data.split(u).join(file);
                                          cb(err);
                                      }
                                  });
                              } else {
                                  s.data = s.data.split(u).join(file);
                                  cb(err);
                              }
                          }
                      });
                  } else {
                      cb();
                  }
              });
              cb();
          }
  
          resolved.Stylesheet.forEach(function(s, index) {
              if (typeof s !== 'string') {
                  if (env == 'development') console.error("[millstone] processing style '" + s.id + "'");
                  return localizeCartoURIs(s,next);
              }
              var uri = url.parse(s);
  
              // URL, download.
              if (uri.protocol && (uri.protocol == 'http:' || uri.protocol == 'https:')) {
                  return request({
                      url: s,
                      proxy: process.env.HTTP_PROXY
                  }, function(err, response, data) {
                      if (err) return next(err);
  
                      resolved.Stylesheet[index] = {
                          id: path.basename(uri.pathname),
                          data: data.toString()
                      };
                      localizeCartoURIs(resolved.Stylesheet[index],next);
                  });
              }
  
              // File, read from disk.
              if (uri.pathname && isRelative(uri.pathname)) {
                  uri.pathname = path.join(base, uri.pathname);
              }
              fs.readFile(uri.pathname, 'utf8', function(err, data) {
                  if (err) return next(err);
  
                  resolved.Stylesheet[index] = {
                      id: s,
                      data: data
                  };
                  localizeCartoURIs(resolved.Stylesheet[index],next);
              });
          });
      }, function layer_externals(err) {
          if (err) throw err;
          if (benchmark) {
              console.timeEnd("[millstone][benchmark] Resolving style (mss) externals");
              console.time("[millstone][benchmark] Resolving layer (mml) externals");
          }
          var remaining = mml.Layer.length;
          var error = null;
          var next = function(err) {
              remaining--;
              if (err && err.code !== 'EEXIST') error = err;
              if (remaining <= 0) this(error);
          }.bind(this);
          if (!remaining) return this();
          resolved.Layer.forEach(function(l, index) {
              if (!l.Datasource || !l.Datasource.file) return next();
              if (env == 'development') console.error("[millstone] processing layer '" + l.name + "'");
  
              // TODO find  better home for this check.
              if (l.Datasource.ttl) checkTTL(cache, l);
  
              var name = l.name || 'layer-' + index;
              var uri = url.parse(encodeURI(l.Datasource.file));
              var pathname = decodeURI(uri.pathname);
              var extname = path.extname(pathname);
              // unbreak pathname on windows if absolute path is used
              // to an alternative drive like e:/
              // https://github.com/mapbox/millstone/issues/81
              if (process.platform === 'win32') {
                  if (uri.protocol && l.Datasource.file.slice(0,2).match(/^[a-zA-Z]:/)) {
                      pathname = l.Datasource.file;
                  }
              }
              // This function takes (egregious) advantage of scope;
              // l, extname, and more is all up-one-level.
              //
              // `file`: filename to be symlinked in place to l.Datasource.file
              var processFile = function(file, cb) {
                  if (!file) return cb();
  
                  readExtension(file, function(err, ext) {
                      // ignore errors from extension check
                      //if (err) console.error(err);
  
                      ext = ext || extname;
  
                      switch (ext.toLowerCase()) {
                      // Unzip and symlink to directory.
                      case '.zip':
                          if (nosymlink) {
                              unzip(file, function(err, file) {
                                  if (err) return cb(err);
                                  l.Datasource.file = file;
                                  return cb();
                              });
                          } else {
                              unzip(file, function(err, file_found) {
                                  if (err) return cb(err);
                                  l.Datasource.file = path.join(base,
                                                        'layers',
                                                        name,
                                                        path.basename(file_found));
                                  return utils.processFiles(file_found,
                                                          l.Datasource.file,
                                                          file_linking_method,
                                                          {cache:cache}, cb);
                              });
                          }
                          break;
                      case '.shp':
                          if (nosymlink) {
                              l.Datasource.file = file;
                              return cb();
                          } else {
                              l.Datasource.file =
                                  path.join(base, 'layers', name, path.basename(file));
                              return utils.processFiles(file, l.Datasource.file, file_linking_method, {cache:cache}, cb);
                          }
                          break;
                      default:
                          if (nosymlink) {
                              l.Datasource.file = file;
                              return cb();
                          } else {
                              l.Datasource.file =
                                  path.join(base, 'layers', name + ext);
                              return file_linking_method(file, l.Datasource.file, {cache:cache}, cb);
                          }
                          break;
                      }
                  });
              };
  
              // URL.
              if (uri.protocol && (uri.protocol == 'http:' || uri.protocol == 'https:')) {
                  var filepath = path.join(cache, cachepath(l.Datasource.file));
                  localize(uri.href, {filepath:filepath,name:l.name}, function(err, file) {
                      if (err) return next(err);
                      processFile(file, next);
                  });
              // Absolute path.
              } else if (pathname && !isRelative(pathname)) {
                  existsAsync(pathname, function(exists) {
                      if (!exists && nosymlink) {
                          // throw here before we try to symlink to avoid confusing error message
                          // we only throw here on nosymlink because a tarred/symlink resolved project
                          // may have locally resolved files that exist, see:
                          // https://github.com/mapbox/tilemill/issues/697#issuecomment-6813928
                          return next(new Error("File not found at absolute path: '" + pathname + "'"));
                      } else {
                          processFile(pathname, next);
                      }
                  });
              // Local path.
              } else {
                  var local_pathname = path.resolve(path.join(base, pathname));
                  // NOTE : we do not call processFile here to avoid munging the name
                  if (path.extname(local_pathname) === '.zip') {
                      unzip(local_pathname, function(err, file) {
                          if (err) return next(err);
                          l.Datasource.file = file;
                          return next();
                      });
                  } else {
                      l.Datasource.file = local_pathname;
                      return next();
                  }
              }
          });
      }, function processSql(err) {
          if (err) throw err;
          if (benchmark) console.timeEnd("[millstone][benchmark] Resolving layer (mml) externals");
          var group = this.group();
          resolved.Layer.forEach(function(l, index) {
              var d = l.Datasource;
              // mapnik's sqlite plugin resolves attached databases
              // relative to the main database, but in tilemill we prefer
              // to interpret relative to the project so we resolve here
              if (d.type == 'sqlite' && d.table && d.attachdb) {
                  var next = group();
                  var dbs = d.attachdb.split(',');
                  Step(function() {
                      var group = this.group();
                      for (var i = 0; i < dbs.length; i++) (function(next) {
                          if (!dbs[i]) {
                              return next();
                          }
  
                          var file = url.parse(dbs[i].split('@').pop());
                          var pathname = file.pathname;
                          var extname = path.extname(pathname);
                          var alias = dbs[i].split('@').shift();
                          var name = (l.name || 'layer-' + index) + '-attach-' + alias;
                          var db_index = i;
  
                          var symlink_db = function(filepath, cb) {
                              var filename = path.join(base, 'layers', name + extname);
                              dbs[db_index] =  alias + '@' + filename;
                              file_linking_method(filepath, filename, {cache:cache}, cb);
                          };
  
                          // URL.
                          if (file.protocol) {
                              var filepath = path.join(cache, cachepath(file.href));
                              localize(file.href, {filepath:filepath,name:name}, function(err) {
                                  if (err) return next(err);
                                  if (nosymlink) {
                                      dbs[db_index] = alias + '@' + filepath;
                                      next();
                                  } else {
                                      symlink_db(filepath, next);
                                  }
                              });
                          }
                          // Absolute path.
                          else if (pathname && !isRelative(pathname)) {
                              if (nosymlink) {
                                  dbs[db_index] = alias + '@' + pathname;
                                  next();
                              } else {
                                  symlink_db(pathname, next);
                              }
                          }
                          // Local path.
                          else {
                              dbs[db_index] =  alias + '@' + path.join(base, pathname);
                              next();
                          }
                      })(group());
                  }, function(err) {
                      if (err) return next(err);
                      d.attachdb = dbs.join(',');
                      return next(err);
                  });
              }
          });
      }, function autodetect(err) {
          if (err) throw err;
          if (benchmark) console.time("[millstone][benchmark] detecting layer type and srs");
          var group = this.group();
          resolved.Layer.forEach(function(l, index) {
              var d = l.Datasource;
              var next = group();
              if (!d.file) return next();
              existsAsync(d.file, function(exists) {
                  if (!exists) {
                      // https://github.com/mapbox/tilemill/issues/1808
                      // on OS X broken symlinks can be read and resolved but actually
                      // do not "exist" so here we try to resolve in order to avoid
                      // providing a confusing error that says a file does not exist
                      // when it actually does (and is just a broken link)
                      fs.readlink(d.file,function(err,resolvedPath){
                          if (resolvedPath) {
                              return next(new Error("File not found: '" +
                                resolvedPath + "' (broken symlink: '" +
                                d.file + "')"));
                          } else {
                              return next(new Error("File not found: '" + d.file + "'"));
                          }
                      });
                  } else {
                      Step(function() {
                          var ext = path.extname(d.file);
                          var next = this;
                          if (ext) {
                              next(null, ext);
                          } else {
                              // This file doesn't have an extension, so we look for a
                              // hidden metadata file that will contain headers for the
                              // original HTTP request. We look at the
                              // `content-disposition` header to determine the extension.
                              fs.lstat(l.Datasource.file, function(err, stats) {
                                  if (err && err.code != 'ENOENT') {
                                      next(err);
                                  } else {
                                      if (!stats.isSymbolicLink()) {
                                          readExtension(l.Datasource.file, next);
                                      } else {
                                          fs.readlink(l.Datasource.file, function(err, resolvedPath) {
                                              if (resolvedPath && isRelative(resolvedPath)) {
                                                  resolvedPath = path.join(path.dirname(l.Datasource.file), resolvedPath);
                                              }
                                              readExtension(resolvedPath, next);
                                          });
                                      }
                                  }
                              });
                          }
                      }, function(err, ext) {
                          // Ignore errors during extension checks above and let a
                          // missing extension fall through to a missing `type`.
                          var name = l.name || 'layer-' + index;
                          ext = ext || path.extname(d.file);
                          d.type = d.type || valid_ds_extensions[ext.toLowerCase()];
                          switch (ext.toLowerCase()) {
                          case '.vrt':
                              // we default to assuming gdal raster for vrt's
                              // but we need to support OGRVRTLayer as well
                              try {
                                  var vrt_file = fs.readFileSync(d.file, 'utf8').toString();
                                  if (vrt_file.indexOf('OGRVRTLayer') != -1) {
                                      d.type = 'ogr';
                                      if (!l.srs) {
                                          var match = vrt_file.match(/<LayerSRS>(.+)<\/LayerSRS>/);
                                          if (match && match[1]) {
                                             var srs_parsed = srs.parse(match[1]);
                                             l.srs = srs_parsed.proj4;
                                          }
                                      }
                                  }
                              } catch (e) {
                                  if (env == 'development') console.error('failed to open vrt file: ' + e.message);
                              }
                              break;
                          case '.csv':
                          case '.tsv':
                          case '.txt':
                          case '.osm':
                          case '.gpx':
                              l.srs = l.srs || SRS.WGS84;
                              break;
                          case '.geojson':
                          case '.topojson':
                          case '.json':
                              try {
                                  var json_obj = JSON.parse(fs.readFileSync(d.file));
                                  l.srs = l.srs || srs.parse(srs.jsonCrs(json_obj)).proj4;
                              } catch (e) {
                                  next(new Error("Could not parse: '" + d.file + "': error: '" + e.message + "'"));
                              }
                              break;
                          case '.kml':
                          case '.rss':
                              l.srs = SRS.WGS84;
                              break;
                          }
                          if (d.type === 'ogr' && !d.layer) {
                              d.layer_by_index = 0;
                          }
                          // at this point if we do not know the 'type' of mapnik
                          // plugin to dispatch to we are out of luck and there is no
                          // need to check for the projection
                          if (!d.type) {
                              return next(new Error("Could not detect datasource type for: '"+d.file+"'"))
                          }
                          if (l.srs) return next();
                          var error = new Error('Unable to determine SRS for layer "' + name + '" at ' + d.file);
                          if (d.type !== 'shape') {
                              // If we don't have a projection by now, bail out unless we have a shapefile.
                              return next(error);
                          } else {
                              // Special handling that opens .prj files for shapefiles.
                              var prj_path = path.join(
                                  path.dirname(d.file),
                                  path.basename(d.file, path.extname(d.file)) + '.prj'
                              );
                              fs.readFile(prj_path, 'utf8', function(err, data) {
                                  if (err && err.code === 'ENOENT') {
                                      return next(error);
                                  } else if (err) {
                                      return next(err);
                                  }
                                  try {
                                      l.srs = l.srs || srs.parse(data).proj4;
                                      l.srs = l.srs || srs.parse('ESRI::' + data).proj4; // See issue #26.
                                  } catch (e) {
                                      next(e);
                                  }
                                  next(l.srs ? null : error);
                              });
                          }
                      });
                  }
              });
          });
      }, function end(err) {
          if (benchmark) console.timeEnd("[millstone][benchmark] detecting layer type and srs");
          // Fix map & layer SRS values.
          resolved.srs = resolved.srs || SRS['900913'];
          fixSRS(resolved);
          resolved.Layer.forEach(fixSRS);
          if (!err && env == 'development') console.error("[millstone] finished processing '" + options.base + "'");
          if (Object.keys(downloads).length < 1) {
              clearInterval(download_log_interval);
              download_log_interval = null;
          }
          return callback(err, resolved);
      });
  }