blob: a38f195acbe91ea1fa986d9aa401fd0411c2bc50 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:args/args.dart';
import 'package:dev_compiler/src/kernel/target.dart';
import 'package:front_end/compilation_message.dart';
import 'package:front_end/compiler_options.dart';
import 'package:front_end/src/kernel_generator_impl.dart';
import 'package:front_end/src/base/processed_options.dart';
import 'package:kernel/kernel.dart';
import 'package:kernel/core_types.dart';
import 'package:path/path.dart' as path;
import '../compiler/module_builder.dart';
import '../compiler/js_names.dart' as JS;
import '../js_ast/js_ast.dart' as JS;
import 'compiler.dart';
import 'native_types.dart';
/// Invoke the compiler with [args].
///
/// Returns `true` if the program compiled without any fatal errors.
Future<bool> compile(List<String> args) async {
var argParser = new ArgParser(allowTrailingOptions: true)
..addOption('out', abbr: 'o', help: 'Output file (required).')
..addOption('dart-sdk-summary',
help: 'The path to the Dart SDK summary file.', hide: true)
..addOption('summary',
abbr: 's', help: 'summaries to link to', allowMultiple: true);
addModuleFormatOptions(argParser, singleOutFile: false);
var declaredVariables = parseAndRemoveDeclaredVariables(args);
var argResults = argParser.parse(args);
var moduleFormat = parseModuleFormatOption(argResults).first;
var ddcPath = path.dirname(path.dirname(path.fromUri(Platform.script)));
var summaryUris =
(argResults['summary'] as List<String>).map(Uri.parse).toList();
var sdkSummaryPath = argResults['dart-sdk-summary'] ??
path.absolute(ddcPath, 'lib', 'sdk', 'ddc_sdk.dill');
var succeeded = true;
void errorHandler(CompilationMessage error) {
// TODO(jmesserly): front end warning levels do not seem to follow the
// Strong Mode/Dart 2 spec. So for now, we treat all warnings as
// compile time errors.
if (error.severity == Severity.error ||
error.severity == Severity.warning) {
succeeded = false;
}
}
var options = new CompilerOptions()
..sdkSummary = path.toUri(sdkSummaryPath)
..packagesFileUri =
path.toUri(path.absolute(ddcPath, '..', '..', '.packages'))
..inputSummaries = summaryUris
..target = new DevCompilerTarget()
..onError = errorHandler
..chaseDependencies = true
..reportMessages = true;
var inputs = argResults.rest
.map((a) => a.startsWith('package:') || a.startsWith('dart:')
? Uri.parse(a)
: path.toUri(path.absolute(a)))
.toList();
String output = argResults['out'];
//var program = await kernelForBuildUnit(inputs, options);
// TODO(jmesserly): use public APIs. For now we need this to access processed
// options, which has info needed to compute library -> module mapping without
// re-parsing inputs.
var processedOpts = new ProcessedOptions(options, true, inputs);
var compilerResult = await generateKernel(processedOpts);
var program = compilerResult?.program;
var sdkSummary = await processedOpts.loadSdkSummary(null);
var nameRoot = sdkSummary?.root ?? new CanonicalName.root();
var summaries = await processedOpts.loadInputSummaries(nameRoot);
if (succeeded) {
var file = new File(output);
if (!file.parent.existsSync()) file.parent.createSync(recursive: true);
// Useful for debugging:
writeProgramToText(program, path: output + '.txt');
// TODO(jmesserly): Save .dill file so other modules can link in this one.
//await writeProgramToBinary(program, output);
var jsModule =
compileToJSModule(program, summaries, summaryUris, declaredVariables);
var jsCode = jsProgramToString(jsModule, moduleFormat);
file.writeAsStringSync(jsCode);
}
return succeeded;
}
JS.Program compileToJSModule(Program p, List<Program> summaries,
List<Uri> summaryUris, Map<String, String> declaredVariables) {
var compiler = new ProgramCompiler(new NativeTypeSet(p, new CoreTypes(p)),
declaredVariables: declaredVariables);
return compiler.emitProgram(p, summaries, summaryUris);
}
String jsProgramToString(JS.Program moduleTree, ModuleFormat format) {
var opts = new JS.JavaScriptPrintingOptions(
allowKeywordsInProperties: true, allowSingleLineIfStatements: true);
// TODO(jmesserly): Support source maps.
var printer = new JS.SimpleJavaScriptPrintingContext();
var tree = transformModuleFormat(format, moduleTree);
tree.accept(
new JS.Printer(opts, printer, localNamer: new JS.TemporaryNamer(tree)));
return printer.getText();
}
/// Parses Dart's non-standard `-Dname=value` syntax for declared variables,
/// and removes them from [args] so the result can be parsed normally.
Map<String, String> parseAndRemoveDeclaredVariables(List<String> args) {
var declaredVariables = <String, String>{};
for (int i = 0; i < args.length;) {
var arg = args[i];
if (arg.startsWith('-D') && arg.length > 2) {
var rest = arg.substring(2);
var eq = rest.indexOf('=');
if (eq <= 0) {
var kind = eq == 0 ? 'name' : 'value';
throw new FormatException('no $kind given to -D option `$arg`');
}
var name = rest.substring(0, eq);
var value = rest.substring(eq + 1);
declaredVariables[name] = value;
args.removeAt(i);
} else {
i++;
}
}
return declaredVariables;
}