Future compile()

in pkg/compiler/lib/src/dart2js.dart [100:1072]


Future<api.CompilationResult> compile(List<String> argv,
    {fe.InitializedCompilerState kernelInitializedCompilerState}) {
  Stopwatch wallclock = Stopwatch()..start();
  stackTraceFilePrefix = '${Uri.base}';
  Uri entryUri;
  Uri inputDillUri;
  Uri librariesSpecificationUri = Uri.base.resolve('lib/libraries.json');
  bool outputSpecified = false;
  Uri out;
  Uri sourceMapOut;
  Uri writeModularAnalysisUri;
  Uri readDataUri;
  Uri writeDataUri;
  Uri readClosedWorldUri;
  Uri writeClosedWorldUri;
  Uri readCodegenUri;
  Uri writeCodegenUri;
  int codegenShard;
  int codegenShards;
  List<String> bazelPaths;
  List<Uri> multiRoots;
  String multiRootScheme = 'org-dartlang-app';
  Uri packageConfig = null;
  List<String> options = <String>[];
  bool wantHelp = false;
  bool wantVersion = false;
  bool trustTypeAnnotations = false;
  bool checkedMode = false;
  bool strongMode = true;
  List<String> hints = <String>[];
  bool verbose;
  bool throwOnError;
  int throwOnErrorCount;
  bool showWarnings;
  bool showHints;
  bool enableColors;
  int optimizationLevel = null;
  Uri platformBinaries;
  Map<String, String> environment = Map<String, String>();
  ReadStrategy readStrategy = ReadStrategy.fromDart;
  WriteStrategy writeStrategy = WriteStrategy.toJs;
  FeatureOptions features = FeatureOptions();
  String invoker;

  void passThrough(String argument) => options.add(argument);
  void ignoreOption(String argument) {}

  if (BUILD_ID != null) {
    passThrough("--build-id=$BUILD_ID");
  }

  Uri extractResolvedFileUri(String argument) {
    return Uri.base.resolve(extractPath(argument, isDirectory: false));
  }

  void setEntryUri(String argument) {
    entryUri = extractResolvedFileUri(argument);
  }

  void setInputDillUri(String argument) {
    inputDillUri = extractResolvedFileUri(argument);
  }

  void setLibrarySpecificationUri(String argument) {
    librariesSpecificationUri = extractResolvedFileUri(argument);
  }

  void setPackageConfig(String argument) {
    packageConfig = extractResolvedFileUri(argument);
  }

  void setOutput(Iterator<String> arguments) {
    outputSpecified = true;
    String path;
    if (arguments.current == '-o') {
      if (!arguments.moveNext()) {
        helpAndFail('Error: Missing file after -o option.');
      }
      path = arguments.current;
    } else {
      path = extractParameter(arguments.current);
    }
    out = Uri.base.resolve(fe.nativeToUriPath(path));
  }

  void setOptimizationLevel(String argument) {
    int value = int.tryParse(extractParameter(argument));
    if (value == null || value < 0 || value > 4) {
      helpAndFail("Error: Unsupported optimization level '$argument', "
          "supported levels are: 0, 1, 2, 3, 4");
      return;
    }
    if (optimizationLevel != null) {
      print("Optimization level '$argument' ignored "
          "due to preceding '-O$optimizationLevel'");
      return;
    }
    optimizationLevel = value;
  }

  void setOutputType(String argument) {
    if (argument == '--output-type=dart' ||
        argument == '--output-type=dart-multi') {
      helpAndFail(
          "--output-type=dart is no longer supported. It was deprecated "
          "since Dart 1.11 and removed in Dart 1.19.");
    }
  }

  setStrip(String argument) {
    helpAndFail("Option '--force-strip' is not in use now that"
        "--output-type=dart is no longer supported.");
  }

  void setBazelPaths(String argument) {
    String paths = extractParameter(argument);
    bazelPaths = <String>[]..addAll(paths.split(','));
  }

  void setMultiRoots(String argument) {
    String paths = extractParameter(argument);
    multiRoots ??= <Uri>[];
    multiRoots.addAll(paths.split(',').map(fe.nativeToUri));
  }

  void setMultiRootScheme(String argument) {
    multiRootScheme = extractParameter(argument);
  }

  String getDepsOutput(Iterable<Uri> sourceFiles) {
    var filenames = sourceFiles.map((uri) => '$uri').toList();
    filenames.sort();
    return filenames.join("\n");
  }

  void setAllowNativeExtensions(String argument) {
    helpAndFail("Option '${Flags.allowNativeExtensions}' is not supported.");
  }

  void setVerbose(_) {
    verbose = true;
    passThrough('--verbose');
  }

  void setTrustTypeAnnotations(String argument) {
    trustTypeAnnotations = true;
  }

  void setCheckedMode(String argument) {
    checkedMode = true;
    passThrough(argument);
  }

  void addInEnvironment(Iterator<String> arguments) {
    final isDefine = arguments.current.startsWith('--define');
    String argument;
    if (arguments.current == '--define') {
      arguments.moveNext();
      argument = arguments.current;
    } else {
      argument = arguments.current.substring(isDefine ? '--define='.length : 2);
    }
    // Allow for ' ' or '=' after --define
    int eqIndex = argument.indexOf('=');
    if (eqIndex <= 0) {
      helpAndFail('Invalid value for --define: $argument');
      return;
    }
    String name = argument.substring(0, eqIndex);
    String value = argument.substring(eqIndex + 1);
    environment[name] = value;
  }

  void setCategories(String argument) {
    List<String> categories = extractParameter(argument).split(',');
    bool isServerMode = categories.length == 1 && categories.single == "Server";
    if (isServerMode) {
      hints.add("The --categories flag is deprecated and will be deleted in a "
          "future release, please use '${Flags.serverMode}' instead of "
          "'--categories=Server'.");
      passThrough(Flags.serverMode);
    } else {
      hints.add(
          "The --categories flag is deprecated, see the usage for details.");
    }
  }

  void setPlatformBinaries(String argument) {
    platformBinaries =
        Uri.base.resolve(extractPath(argument, isDirectory: true));
  }

  void setUriList(String flag, String argument) {
    String list = extractParameter(argument);
    String uriList = list.splitMapJoin(',',
        onMatch: (_) => ',', onNonMatch: (p) => '${fe.nativeToUri(p)}');
    options.add('${flag}=${uriList}');
  }

  void setModularAnalysisInputs(String argument) {
    setUriList(Flags.readModularAnalysis, argument);
  }

  void setWriteModularAnalysis(String argument) {
    if (writeStrategy == WriteStrategy.toClosedWorld) {
      fail("Cannot use ${Flags.writeModularAnalysis} "
          "and write serialized closed world simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toData) {
      fail("Cannot use ${Flags.writeModularAnalysis} "
          "and write serialized global data simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toCodegen) {
      fail("Cannot use ${Flags.writeModularAnalysis} "
          "and write serialized codegen simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toKernel) {
      fail("Cannot use ${Flags.writeModularAnalysis} "
          "and run the CFE simultaneously.");
    }
    if (argument != Flags.writeModularAnalysis) {
      writeModularAnalysisUri =
          fe.nativeToUri(extractPath(argument, isDirectory: false));
    }
    writeStrategy = WriteStrategy.toModularAnalysis;
  }

  void setReadData(String argument) {
    if (argument != Flags.readData) {
      readDataUri = fe.nativeToUri(extractPath(argument, isDirectory: false));
    }

    if (readStrategy == ReadStrategy.fromDart) {
      readStrategy = ReadStrategy.fromData;
    } else if (readStrategy == ReadStrategy.fromClosedWorld) {
      readStrategy = ReadStrategy.fromDataAndClosedWorld;
    } else if (readStrategy == ReadStrategy.fromCodegen) {
      readStrategy = ReadStrategy.fromCodegenAndData;
    } else if (readStrategy == ReadStrategy.fromCodegenAndClosedWorld) {
      readStrategy = ReadStrategy.fromCodegenAndClosedWorldAndData;
    }
  }

  void setReadClosedWorld(String argument) {
    if (argument != Flags.readClosedWorld) {
      readClosedWorldUri =
          fe.nativeToUri(extractPath(argument, isDirectory: false));
    }

    if (readStrategy == ReadStrategy.fromDart) {
      readStrategy = ReadStrategy.fromClosedWorld;
    } else if (readStrategy == ReadStrategy.fromData) {
      readStrategy = ReadStrategy.fromDataAndClosedWorld;
    } else if (readStrategy == ReadStrategy.fromCodegen) {
      readStrategy = ReadStrategy.fromCodegenAndClosedWorld;
    } else if (readStrategy == ReadStrategy.fromCodegenAndData) {
      readStrategy = ReadStrategy.fromCodegenAndClosedWorldAndData;
    }
  }

  void setDillDependencies(String argument) {
    setUriList(Flags.dillDependencies, argument);
  }

  void setCfeOnly(String argument) {
    if (writeStrategy == WriteStrategy.toModularAnalysis) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized modular analysis simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toClosedWorld) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized closed world simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toData) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized data simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toCodegen) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized codegen simultaneously.");
    }
    writeStrategy = WriteStrategy.toKernel;
  }

  void setReadCodegen(String argument) {
    if (argument != Flags.readCodegen) {
      readCodegenUri =
          fe.nativeToUri(extractPath(argument, isDirectory: false));
    }

    if (readStrategy == ReadStrategy.fromDart) {
      readStrategy = ReadStrategy.fromCodegen;
    } else if (readStrategy == ReadStrategy.fromClosedWorld) {
      readStrategy = ReadStrategy.fromCodegenAndClosedWorld;
    } else if (readStrategy == ReadStrategy.fromData) {
      readStrategy = ReadStrategy.fromCodegenAndData;
    } else if (readStrategy == ReadStrategy.fromDataAndClosedWorld) {
      readStrategy = ReadStrategy.fromCodegenAndClosedWorldAndData;
    }
  }

  void setWriteData(String argument) {
    if (writeStrategy == WriteStrategy.toKernel) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized data simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toClosedWorld) {
      fail("Cannot write closed world and data simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toCodegen) {
      fail("Cannot write serialized data and codegen simultaneously.");
    }
    if (argument != Flags.writeData) {
      writeDataUri = fe.nativeToUri(extractPath(argument, isDirectory: false));
    }
    writeStrategy = WriteStrategy.toData;
  }

  void setWriteClosedWorld(String argument) {
    if (writeStrategy == WriteStrategy.toKernel) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized data simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toData) {
      fail("Cannot write both closed world and data");
    }
    if (writeStrategy == WriteStrategy.toCodegen) {
      fail("Cannot write serialized data and codegen simultaneously.");
    }
    if (argument != Flags.writeClosedWorld) {
      writeClosedWorldUri =
          fe.nativeToUri(extractPath(argument, isDirectory: false));
    }
    writeStrategy = WriteStrategy.toClosedWorld;
  }

  void setWriteCodegen(String argument) {
    if (writeStrategy == WriteStrategy.toKernel) {
      fail("Cannot use ${Flags.cfeOnly} "
          "and write serialized codegen simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toClosedWorld) {
      fail("Cannot write closed world and codegen simultaneously.");
    }
    if (writeStrategy == WriteStrategy.toData) {
      fail("Cannot write serialized data and codegen data simultaneously.");
    }
    if (argument != Flags.writeCodegen) {
      writeCodegenUri =
          fe.nativeToUri(extractPath(argument, isDirectory: false));
    }
    writeStrategy = WriteStrategy.toCodegen;
  }

  void setCodegenShard(String argument) {
    codegenShard = int.parse(extractParameter(argument));
  }

  void setCodegenShards(String argument) {
    codegenShards = int.parse(extractParameter(argument));
  }

  void setDumpInfo(String argument) {
    passThrough(Flags.dumpInfo);
    if (argument == Flags.dumpInfo || argument == "${Flags.dumpInfo}=json") {
      return;
    }
    if (argument == "${Flags.dumpInfo}=binary") {
      passThrough(argument);
      return;
    }
    helpAndFail("Error: Unsupported dump-info format '$argument', "
        "supported formats are: json or binary");
  }

  String nullSafetyMode = null;
  void setNullSafetyMode(String argument) {
    if (nullSafetyMode != null && nullSafetyMode != argument) {
      helpAndFail("Error: cannot specify both $nullSafetyMode and $argument.");
    }
    nullSafetyMode = argument;
    passThrough(argument);
  }

  void setInvoker(String argument) {
    invoker = extractParameter(argument);
  }

  void handleThrowOnError(String argument) {
    throwOnError = true;
    String parameter = extractParameter(argument, isOptionalArgument: true);
    if (parameter != null) {
      var count = int.parse(parameter);
      throwOnErrorCount = count;
    }
  }

  void handleShortOptions(String argument) {
    var shortOptions = argument.substring(1).split("");
    for (var shortOption in shortOptions) {
      switch (shortOption) {
        case 'v':
          setVerbose(null);
          break;
        case 'h':
        case '?':
          wantHelp = true;
          break;
        case 'c':
          setCheckedMode(Flags.enableCheckedMode);
          break;
        case 'm':
          passThrough(Flags.minify);
          break;
        default:
          throw 'Internal error: "$shortOption" did not match';
      }
    }
  }

  List<String> arguments = <String>[];
  List<OptionHandler> handlers = <OptionHandler>[
    OptionHandler('${Flags.entryUri}=.+', setEntryUri),
    OptionHandler('${Flags.inputDill}=.+', setInputDillUri),
    OptionHandler('-[chvm?]+', handleShortOptions),
    OptionHandler('--throw-on-error(?:=[0-9]+)?', handleThrowOnError),
    OptionHandler(Flags.suppressWarnings, (String argument) {
      showWarnings = false;
      passThrough(argument);
    }),
    OptionHandler(Flags.fatalWarnings, passThrough),
    OptionHandler(Flags.suppressHints, (String argument) {
      showHints = false;
      passThrough(argument);
    }),
    // TODO(sigmund): remove entirely after Dart 1.20
    OptionHandler(
        '--output-type=dart|--output-type=dart-multi|--output-type=js',
        setOutputType),
    OptionHandler('--use-kernel', ignoreOption),
    OptionHandler(Flags.platformBinaries, setPlatformBinaries),
    OptionHandler(Flags.noFrequencyBasedMinification, passThrough),
    OptionHandler(Flags.verbose, setVerbose),
    OptionHandler(Flags.progress, passThrough),
    OptionHandler(Flags.reportMetrics, passThrough),
    OptionHandler(Flags.reportAllMetrics, passThrough),
    OptionHandler(Flags.version, (_) => wantVersion = true),
    OptionHandler('--library-root=.+', ignoreOption),
    OptionHandler('--libraries-spec=.+', setLibrarySpecificationUri),
    OptionHandler('${Flags.dillDependencies}=.+', setDillDependencies),
    OptionHandler('${Flags.readModularAnalysis}=.+', setModularAnalysisInputs),
    OptionHandler(
        '${Flags.writeModularAnalysis}|${Flags.writeModularAnalysis}=.+',
        setWriteModularAnalysis),
    OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
    OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
    OptionHandler(Flags.noClosedWorldInData, ignoreOption),
    OptionHandler('${Flags.readClosedWorld}|${Flags.readClosedWorld}=.+',
        setReadClosedWorld),
    OptionHandler('${Flags.writeClosedWorld}|${Flags.writeClosedWorld}=.+',
        setWriteClosedWorld),
    OptionHandler(
        '${Flags.readCodegen}|${Flags.readCodegen}=.+', setReadCodegen),
    OptionHandler(
        '${Flags.writeCodegen}|${Flags.writeCodegen}=.+', setWriteCodegen),
    OptionHandler('${Flags.codegenShard}=.+', setCodegenShard),
    OptionHandler('${Flags.codegenShards}=.+', setCodegenShards),
    OptionHandler(Flags.cfeOnly, setCfeOnly),
    OptionHandler(Flags.debugGlobalInference, passThrough),
    OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
    OptionHandler('-O.*', setOptimizationLevel),
    OptionHandler(Flags.allowMockCompilation, ignoreOption),
    OptionHandler(Flags.fastStartup, ignoreOption),
    OptionHandler(Flags.genericMethodSyntax, ignoreOption),
    OptionHandler(Flags.initializingFormalAccess, ignoreOption),
    OptionHandler(Flags.minify, passThrough),
    OptionHandler(Flags.noMinify, passThrough),
    OptionHandler(Flags.omitLateNames, passThrough),
    OptionHandler(Flags.noOmitLateNames, passThrough),
    OptionHandler(Flags.preserveUris, ignoreOption),
    OptionHandler(Flags.printLegacyStars, passThrough),
    OptionHandler('--force-strip=.*', setStrip),
    OptionHandler(Flags.disableDiagnosticColors, (_) {
      enableColors = false;
    }),
    OptionHandler(Flags.enableDiagnosticColors, (_) {
      enableColors = true;
    }),
    OptionHandler('--enable[_-]checked[_-]mode|--checked',
        (_) => setCheckedMode(Flags.enableCheckedMode)),
    OptionHandler(Flags.enableAsserts, passThrough),
    OptionHandler(Flags.enableNullAssertions, passThrough),
    OptionHandler(Flags.nativeNullAssertions, passThrough),
    OptionHandler(Flags.noNativeNullAssertions, passThrough),
    OptionHandler(Flags.trustTypeAnnotations, setTrustTypeAnnotations),
    OptionHandler(Flags.trustPrimitives, passThrough),
    OptionHandler(Flags.trustJSInteropTypeAnnotations, ignoreOption),
    OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
    OptionHandler('--packages=.+', setPackageConfig),
    OptionHandler(Flags.noSourceMaps, passThrough),
    OptionHandler(Option.resolutionInput, ignoreOption),
    OptionHandler(Option.bazelPaths, setBazelPaths),
    OptionHandler(Option.multiRoots, setMultiRoots),
    OptionHandler(Option.multiRootScheme, setMultiRootScheme),
    OptionHandler(Flags.resolveOnly, ignoreOption),
    OptionHandler(Flags.disableNativeLiveTypeAnalysis, passThrough),
    OptionHandler('--categories=.*', setCategories),
    OptionHandler(Flags.serverMode, passThrough),
    OptionHandler(Flags.disableInlining, passThrough),
    OptionHandler(Flags.disableProgramSplit, passThrough),
    OptionHandler(Flags.stopAfterProgramSplit, passThrough),
    OptionHandler(Flags.disableTypeInference, passThrough),
    OptionHandler(Flags.useTrivialAbstractValueDomain, passThrough),
    OptionHandler(Flags.experimentalWrapped, passThrough),
    OptionHandler(Flags.experimentalPowersets, passThrough),
    OptionHandler(Flags.disableRtiOptimization, passThrough),
    OptionHandler(Flags.terse, passThrough),
    OptionHandler('--deferred-map=.+', passThrough),
    OptionHandler('${Flags.writeProgramSplit}=.+', passThrough),
    OptionHandler('${Flags.readProgramSplit}=.+', passThrough),
    OptionHandler('${Flags.dumpInfo}|${Flags.dumpInfo}=.+', setDumpInfo),
    OptionHandler('--disallow-unsafe-eval', ignoreOption),
    OptionHandler(Option.showPackageWarnings, passThrough),
    OptionHandler(Option.enableLanguageExperiments, passThrough),
    OptionHandler('--enable-experimental-mirrors', ignoreOption),
    OptionHandler(Flags.enableAssertMessage, passThrough),
    OptionHandler('--strong', ignoreOption),
    OptionHandler(Flags.previewDart2, ignoreOption),
    OptionHandler(Flags.omitImplicitChecks, passThrough),
    OptionHandler(Flags.omitAsCasts, passThrough),
    OptionHandler(Flags.laxRuntimeTypeToString, passThrough),
    OptionHandler(Flags.benchmarkingProduction, passThrough),
    OptionHandler(Flags.benchmarkingExperiment, passThrough),
    OptionHandler(Flags.soundNullSafety, setNullSafetyMode),
    OptionHandler(Flags.noSoundNullSafety, setNullSafetyMode),
    OptionHandler(Flags.dumpUnusedLibraries, passThrough),

    // TODO(floitsch): remove conditional directives flag.
    // We don't provide the info-message yet, since we haven't publicly
    // launched the feature yet.
    OptionHandler(Flags.conditionalDirectives, ignoreOption),
    OptionHandler('--enable-async', ignoreOption),
    OptionHandler('--enable-null-aware-operators', ignoreOption),
    OptionHandler('--enable-enum', ignoreOption),
    OptionHandler(Flags.allowNativeExtensions, setAllowNativeExtensions),
    OptionHandler(Flags.generateCodeWithCompileTimeErrors, ignoreOption),
    OptionHandler(Flags.useMultiSourceInfo, passThrough),
    OptionHandler(Flags.useNewSourceInfo, passThrough),
    OptionHandler(Flags.useOldRti, passThrough),
    OptionHandler(Flags.useSimpleLoadIds, passThrough),
    OptionHandler(Flags.testMode, passThrough),
    OptionHandler('${Flags.dumpSsa}=.+', passThrough),
    OptionHandler('${Flags.cfeInvocationModes}=.+', passThrough),
    OptionHandler('${Flags.invoker}=.+', setInvoker),
    OptionHandler('${Flags.verbosity}=.+', passThrough),

    // Experimental features.
    // We don't provide documentation for these yet.
    // TODO(29574): provide documentation when this feature is supported.
    // TODO(29574): provide a warning/hint/error, when profile-based data is
    // used without `--fast-startup`.
    OptionHandler(Flags.experimentalTrackAllocations, passThrough),

    OptionHandler(Flags.experimentLocalNames, ignoreOption),
    OptionHandler(Flags.experimentStartupFunctions, passThrough),
    OptionHandler(Flags.experimentToBoolean, passThrough),
    OptionHandler(Flags.experimentUnreachableMethodsThrow, passThrough),
    OptionHandler(Flags.experimentCallInstrumentation, passThrough),
    OptionHandler(Flags.experimentNewRti, ignoreOption),
    OptionHandler('${Flags.mergeFragmentsThreshold}=.+', passThrough),

    // Wire up feature flags.
    OptionHandler(Flags.canary, passThrough),
    OptionHandler(Flags.noShipping, passThrough),
    // Shipped features.
    for (var feature in features.shipped)
      OptionHandler('--${feature.flag}', passThrough),
    for (var feature in features.shipped)
      OptionHandler('--no-${feature.flag}', passThrough),
    // Shipping features.
    for (var feature in features.shipping)
      OptionHandler('--${feature.flag}', passThrough),
    for (var feature in features.shipping)
      OptionHandler('--no-${feature.flag}', passThrough),
    // Canary features.
    for (var feature in features.canary)
      OptionHandler('--${feature.flag}', passThrough),
    for (var feature in features.canary)
      OptionHandler('--no-${feature.flag}', passThrough),

    // The following three options must come last.
    OptionHandler('-D.+=.*|--define=.+=.*|--define', addInEnvironment,
        multipleArguments: true),
    OptionHandler('-.*', (String argument) {
      helpAndFail("Unknown option '$argument'.");
    }),
    OptionHandler('.*', (String argument) {
      arguments.add(fe.nativeToUriPath(argument));
    })
  ];

  parseCommandLine(handlers, argv);

  if (invoker == null) {
    warning("The 'dart2js' entrypoint script is deprecated, "
        "please use 'dart compile js' instead.");
  } else if (verbose != null) {
    print("Compiler invoked from: '$invoker'");
  }

  // TODO(johnniwinther): Measure time for reading files.
  SourceFileProvider inputProvider;
  if (bazelPaths != null) {
    if (multiRoots != null) {
      helpAndFail(
          'The options --bazel-root and --multi-root cannot be supplied '
          'together, please choose one or the other.');
    }
    inputProvider = BazelInputProvider(bazelPaths);
  } else if (multiRoots != null) {
    inputProvider = MultiRootInputProvider(multiRootScheme, multiRoots);
  } else {
    inputProvider = CompilerSourceFileProvider();
  }

  diagnosticHandler = FormattingDiagnosticHandler(inputProvider);
  if (verbose != null) {
    diagnosticHandler.verbose = verbose;
  }
  if (throwOnError != null) {
    diagnosticHandler.throwOnError = throwOnError;
  }
  if (throwOnErrorCount != null) {
    diagnosticHandler.throwOnErrorCount = throwOnErrorCount;
  }
  if (showWarnings != null) {
    diagnosticHandler.showWarnings = showWarnings;
  }
  if (showHints != null) {
    diagnosticHandler.showHints = showHints;
  }
  if (enableColors != null) {
    diagnosticHandler.enableColors = enableColors;
  }

  if (checkedMode && strongMode) {
    checkedMode = false;
    hints.add("Option '${Flags.enableCheckedMode}' is not needed in Dart 2.0. "
        "To enable assertions use '${Flags.enableAsserts}' instead.");
  }

  if (trustTypeAnnotations && strongMode) {
    hints.add("Option '${Flags.trustTypeAnnotations}' is not available "
        "in Dart 2.0. Try using '${Flags.omitImplicitChecks}' instead.");
  }

  for (String hint in hints) {
    diagnosticHandler.info(hint, api.Diagnostic.HINT);
  }

  if (wantHelp || wantVersion) {
    helpAndExit(wantHelp, wantVersion, diagnosticHandler.verbose);
  }

  if (arguments.isEmpty && entryUri == null && inputDillUri == null) {
    helpAndFail('No Dart file specified.');
  }

  if (arguments.length > 1) {
    var extra = arguments.sublist(1);
    helpAndFail('Extra arguments: ${extra.join(" ")}');
  }

  if (trustTypeAnnotations && checkedMode) {
    helpAndFail("Option '${Flags.trustTypeAnnotations}' may not be used in "
        "checked mode.");
  }

  if (arguments.isNotEmpty) {
    String sourceOrDill = arguments[0];
    Uri file = Uri.base.resolve(fe.nativeToUriPath(sourceOrDill));
    if (sourceOrDill.endsWith('.dart')) {
      entryUri = file;
    } else {
      assert(sourceOrDill.endsWith('.dill'));
      inputDillUri = file;
    }
  }

  // Make [scriptName] a relative path..
  String scriptName =
      fe.relativizeUri(Uri.base, inputDillUri ?? entryUri, Platform.isWindows);

  switch (writeStrategy) {
    case WriteStrategy.toJs:
      out ??= Uri.base.resolve('out.js');
      break;
    case WriteStrategy.toKernel:
      out ??= Uri.base.resolve('out.dill');
      options.add(Flags.cfeOnly);
      if (readStrategy == ReadStrategy.fromClosedWorld) {
        fail("Cannot use ${Flags.cfeOnly} "
            "and read serialized closed world simultaneously.");
      } else if (readStrategy == ReadStrategy.fromData) {
        fail("Cannot use ${Flags.cfeOnly} "
            "and read serialized data simultaneously.");
      } else if (readStrategy == ReadStrategy.fromCodegen) {
        fail("Cannot use ${Flags.cfeOnly} "
            "and read serialized codegen simultaneously.");
      }
      break;
    case WriteStrategy.toModularAnalysis:
      writeModularAnalysisUri ??= Uri.base.resolve('$out.mdata');
      options.add('${Flags.writeModularAnalysis}=${writeModularAnalysisUri}');
      out ??= Uri.base.resolve('out.dill');
      break;
    case WriteStrategy.toClosedWorld:
      out ??= Uri.base.resolve('out.dill');
      writeClosedWorldUri ??= Uri.base.resolve('$out.world');
      options.add('${Flags.writeClosedWorld}=${writeClosedWorldUri}');
      if (readStrategy == ReadStrategy.fromClosedWorld) {
        fail("Cannot read and write serialized data simultaneously.");
      } else if (readStrategy == ReadStrategy.fromData) {
        fail("Cannot read from both closed world and data");
      } else if (readStrategy == ReadStrategy.fromCodegen) {
        fail("Cannot read serialized codegen and "
            "write serialized data simultaneously.");
      }
      break;
    case WriteStrategy.toData:
      out ??= Uri.base.resolve('out.dill');
      writeDataUri ??= Uri.base.resolve('$out.data');
      options.add('${Flags.writeData}=${writeDataUri}');
      if (readStrategy == ReadStrategy.fromData) {
        fail("Cannot read and write serialized data simultaneously.");
      } else if (readStrategy == ReadStrategy.fromCodegen) {
        fail("Cannot read serialized codegen and "
            "write serialized data simultaneously.");
      }
      break;
    case WriteStrategy.toCodegen:
      // TODO(johnniwinther): Avoid the need for an [out] value in this case or
      // use [out] to pass [writeCodegenUri].
      out ??= Uri.base.resolve('out');
      writeCodegenUri ??= Uri.base.resolve('$out.code');
      options.add('${Flags.writeCodegen}=${writeCodegenUri}');
      if (readStrategy == ReadStrategy.fromCodegen) {
        fail("Cannot read and write serialized codegen simultaneously.");
      }
      if (readStrategy != ReadStrategy.fromDataAndClosedWorld) {
        fail("Can only write serialized codegen from serialized data.");
      }
      if (codegenShards == null) {
        fail("Cannot write serialized codegen without setting "
            "${Flags.codegenShards}.");
      } else if (codegenShards <= 0) {
        fail("${Flags.codegenShards} must be a positive integer.");
      }
      if (codegenShard == null) {
        fail("Cannot write serialized codegen without setting "
            "${Flags.codegenShard}.");
      } else if (codegenShard < 0 || codegenShard >= codegenShards) {
        fail("${Flags.codegenShard} must be between 0 and "
            "${Flags.codegenShards}.");
      }
      options.add('${Flags.codegenShard}=$codegenShard');
      options.add('${Flags.codegenShards}=$codegenShards');
      break;
  }
  switch (readStrategy) {
    case ReadStrategy.fromDart:
      break;
    case ReadStrategy.fromClosedWorld:
      readClosedWorldUri ??= Uri.base.resolve('$scriptName.world');
      options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
      break;
    case ReadStrategy.fromData:
      fail("Must read from closed world and data.");
      break;
    case ReadStrategy.fromDataAndClosedWorld:
      readClosedWorldUri ??= Uri.base.resolve('$scriptName.world');
      options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
      readDataUri ??= Uri.base.resolve('$scriptName.data');
      options.add('${Flags.readData}=${readDataUri}');
      break;
    case ReadStrategy.fromCodegen:
    case ReadStrategy.fromCodegenAndData:
    case ReadStrategy.fromCodegenAndClosedWorld:
      fail("Must read from closed world, data, and codegen");
      break;
    case ReadStrategy.fromCodegenAndClosedWorldAndData:
      readClosedWorldUri ??= Uri.base.resolve('$scriptName.world');
      options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
      readDataUri ??= Uri.base.resolve('$scriptName.data');
      options.add('${Flags.readData}=${readDataUri}');
      readCodegenUri ??= Uri.base.resolve('$scriptName.code');
      options.add('${Flags.readCodegen}=${readCodegenUri}');
      if (codegenShards == null) {
        fail("Cannot write serialized codegen without setting "
            "${Flags.codegenShards}.");
      } else if (codegenShards <= 0) {
        fail("${Flags.codegenShards} must be a positive integer.");
      }
      options.add('${Flags.codegenShards}=$codegenShards');
      break;
  }
  options.add('--out=$out');
  if (writeStrategy == WriteStrategy.toJs) {
    sourceMapOut = Uri.parse('$out.map');
    options.add('--source-map=${sourceMapOut}');
  }

  RandomAccessFileOutputProvider outputProvider =
      RandomAccessFileOutputProvider(out, sourceMapOut,
          onInfo: diagnosticHandler.info, onFailure: fail);

  api.CompilationResult compilationDone(api.CompilationResult result) {
    if (!result.isSuccess) {
      fail('Compilation failed.');
    }
    writeString(
        Uri.parse('$out.deps'), getDepsOutput(inputProvider.getSourceUris()));

    String input = scriptName;
    int inputSize;
    String processName;
    String inputName;

    int outputSize;
    int primaryOutputSize;
    String outputName;

    String summary;
    switch (readStrategy) {
      case ReadStrategy.fromDart:
        inputName = inputDillUri != null ? 'kernel bytes' : 'characters Dart';
        inputSize = inputProvider.dartCharactersRead;
        summary = 'Dart file $input ';
        break;
      case ReadStrategy.fromClosedWorld:
        inputName = 'bytes data';
        inputSize = inputProvider.dartCharactersRead;
        String dataInput =
            fe.relativizeUri(Uri.base, readClosedWorldUri, Platform.isWindows);
        summary = 'Data files $input and $dataInput ';
        break;
      case ReadStrategy.fromData:
        fail("Must read from closed world and data.");
        break;
      case ReadStrategy.fromDataAndClosedWorld:
        inputName = 'bytes data';
        inputSize = inputProvider.dartCharactersRead;
        String worldInput =
            fe.relativizeUri(Uri.base, readClosedWorldUri, Platform.isWindows);
        String dataInput =
            fe.relativizeUri(Uri.base, readDataUri, Platform.isWindows);
        summary = 'Data files $input, $worldInput, and $dataInput ';
        break;
      case ReadStrategy.fromCodegen:
      case ReadStrategy.fromCodegenAndData:
      case ReadStrategy.fromCodegenAndClosedWorld:
        fail("Must read from closed world, data, and codegen");
        break;
      case ReadStrategy.fromCodegenAndClosedWorldAndData:
        inputName = 'bytes data';
        inputSize = inputProvider.dartCharactersRead;
        String worldInput =
            fe.relativizeUri(Uri.base, readClosedWorldUri, Platform.isWindows);
        String dataInput =
            fe.relativizeUri(Uri.base, readDataUri, Platform.isWindows);
        String codeInput =
            fe.relativizeUri(Uri.base, readCodegenUri, Platform.isWindows);
        summary = 'Data files $input, $worldInput, $dataInput and '
            '${codeInput}[0-${codegenShards - 1}] ';
        break;
    }

    switch (writeStrategy) {
      case WriteStrategy.toJs:
        processName = 'Compiled';
        outputName = 'characters JavaScript';
        outputSize = outputProvider.totalCharactersWrittenJavaScript;
        primaryOutputSize = outputProvider.totalCharactersWrittenPrimary;
        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
        summary += 'compiled to JavaScript: ${output}';
        break;
      case WriteStrategy.toKernel:
        processName = 'Compiled';
        outputName = 'kernel bytes';
        outputSize = outputProvider.totalDataWritten;
        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
        summary += 'compiled to dill: ${output}.';
        break;
      case WriteStrategy.toModularAnalysis:
        processName = 'Serialized';
        outputName = 'bytes data';
        outputSize = outputProvider.totalDataWritten;
        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
        String dataOutput = fe.relativizeUri(
            Uri.base, writeModularAnalysisUri, Platform.isWindows);
        summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
        break;
      case WriteStrategy.toClosedWorld:
        processName = 'Serialized';
        outputName = 'bytes data';
        outputSize = outputProvider.totalDataWritten;
        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
        String dataOutput =
            fe.relativizeUri(Uri.base, writeClosedWorldUri, Platform.isWindows);
        summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
        break;
      case WriteStrategy.toData:
        processName = 'Serialized';
        outputName = 'bytes data';
        outputSize = outputProvider.totalDataWritten;
        String output = fe.relativizeUri(Uri.base, out, Platform.isWindows);
        String dataOutput =
            fe.relativizeUri(Uri.base, writeDataUri, Platform.isWindows);
        summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
        break;
      case WriteStrategy.toCodegen:
        processName = 'Serialized';
        outputName = 'bytes data';
        outputSize = outputProvider.totalDataWritten;
        String codeOutput =
            fe.relativizeUri(Uri.base, writeCodegenUri, Platform.isWindows);
        summary += 'serialized to codegen data: '
            '${codeOutput}${codegenShard}.';
        break;
    }

    print('$processName '
        '${_formatCharacterCount(inputSize)} $inputName to '
        '${_formatCharacterCount(outputSize)} $outputName in '
        '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
    if (primaryOutputSize != null) {
      diagnosticHandler
          .info('${_formatCharacterCount(primaryOutputSize)} $outputName '
              'in ${fe.relativizeUri(Uri.base, out, Platform.isWindows)}');
    }
    if (writeStrategy == WriteStrategy.toJs) {
      if (outputSpecified || diagnosticHandler.verbose) {
        print(summary);
        if (diagnosticHandler.verbose) {
          var files = outputProvider.allOutputFiles;
          int jsCount = files.where((f) => f.endsWith('.js')).length;
          print('Emitted file $jsCount JavaScript files.');
        }
      }
    } else {
      print(summary);
    }

    return result;
  }

  diagnosticHandler.autoReadFileUri = true;
  CompilerOptions compilerOptions = CompilerOptions.parse(options,
      featureOptions: features,
      librariesSpecificationUri: librariesSpecificationUri,
      platformBinaries: platformBinaries,
      onError: (String message) => fail(message),
      onWarning: (String message) => print(message))
    ..entryUri = entryUri
    ..inputDillUri = inputDillUri
    ..packageConfig = packageConfig
    ..environment = environment
    ..kernelInitializedCompilerState = kernelInitializedCompilerState
    ..optimizationLevel = optimizationLevel;
  return compileFunc(
          compilerOptions, inputProvider, diagnosticHandler, outputProvider)
      .then(compilationDone);
}