async function main(opt_args) {
  const args = parseArgs(opt_args);

  if (!args.file && !args.root) {
    console.error('Must supply inputs and/or roots.');
    yargs.showHelp();
    process.exit(1);
  }

  const sources = new Set((args.file || []).map(resolve));
  const roots = (args.root || []).map(resolve);
  const excluded = [...new Set(args.exclude || [])].map(resolve);

  const allFiles =
      await Promise.all(roots.map(r => findAllJsFiles(r, excluded)));
  for (const files of allFiles) {
    for (const file of files) {
      sources.add(file);
    }
  }

  const results = await Promise.all([...sources].map(parser.parseFileAsync));
  let fatal = false;
  const errors = [].concat.apply([], results.map(r => r.errors));

  for (const error of errors) {
    fatal = fatal || error.fatal;
  }

  if (fatal) {
    return {errors};
  }

  const deps = [];
  const depsFromDepFiles = new Set();

  for (const result of results) {
    for (const dep of result.dependencies) {
      if (result.isFromDepsFile) {
        depsFromDepFiles.add(dep);
      }
      deps.push(dep);
    }
  }

  const closureDep = await getClosureDep(deps, args);
  const closurePath = path.dirname(closureDep.path);

  // Update the path to closure for any files that we don't know the full path
  // of (parsed from a goog.addDependency call).
  for (const dep of deps) {
    dep.setClosurePath(closurePath);
  }

  if (args.validate) {
    new depGraph.Graph(deps).validate();
  }

  const depsToWrite = args.mergeDeps
      ? deps
      // `deps` - `depsFromFiles`.
      : deps.filter(d => !depsFromDepFiles.has(d));

  const text = depFile.getDepFileText(closurePath, depsToWrite);
  return {errors, text};
}
@param {!Array<string>=} opt_args
@return {!Promise<{errors:!Array<!ParseError>,text:(string|undefined)}>}