[dartdevc] add module-name option to kernel backend

This name is not used by most module formats, but we do use it with the
legacy format.

Change-Id: I8d2f36b12a60b37d0460b57d5a360ba21b4e0476
Reviewed-on: https://dart-review.googlesource.com/c/78923
Reviewed-by: Jake Macdonald <jakemac@google.com>
Commit-Queue: Jenny Messerly <jmesserly@google.com>
This commit is contained in:
Jenny Messerly 2018-10-10 18:09:03 +00:00 committed by commit-bot@chromium.org
parent b8760fed67
commit f320477ff9
9 changed files with 95 additions and 93 deletions

View file

@ -48,7 +48,7 @@ import 'error_helpers.dart';
import 'extension_types.dart' show ExtensionTypeSet;
import 'js_interop.dart';
import 'js_typerep.dart';
import 'module_compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile;
import 'module_compiler.dart' show CompilerOptions, JSModuleFile;
import 'nullable_type_inference.dart' show NullableTypeInference;
import 'property_model.dart';
import 'reify_coercions.dart' show CoercionReifier;
@ -179,8 +179,6 @@ class CodeGenerator extends Object
final _deferredProperties = HashMap<PropertyAccessorElement, JS.Method>();
BuildUnit _buildUnit;
String _libraryRoot;
bool _superAllowed = true;
@ -250,29 +248,29 @@ class CodeGenerator extends Object
///
/// Takes the metadata for the build unit, as well as resolved trees and
/// errors, and computes the output module code and optionally the source map.
JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits) {
_buildUnit = unit;
_libraryRoot = _buildUnit.libraryRoot;
JSModuleFile compile(List<CompilationUnit> compilationUnits) {
_libraryRoot = options.libraryRoot;
if (!_libraryRoot.endsWith(path.separator)) {
_libraryRoot += path.separator;
}
var name = options.moduleName;
invalidModule() =>
JSModuleFile.invalid(unit.name, formatErrors(context, errors), options);
JSModuleFile.invalid(name, formatErrors(context, errors), options);
if (!options.unsafeForceCompile && errors.any(_isFatalError)) {
return invalidModule();
}
try {
var module = _emitModule(compilationUnits, unit.name);
var module = _emitModule(compilationUnits, name);
if (!options.unsafeForceCompile && errors.any(_isFatalError)) {
return invalidModule();
}
var dartApiSummary = _summarizeModule(compilationUnits);
return JSModuleFile(unit.name, formatErrors(context, errors), options,
module, dartApiSummary);
return JSModuleFile(
name, formatErrors(context, errors), options, module, dartApiSummary);
} catch (e) {
if (errors.any(_isFatalError)) {
// Force compilation failed. Suppress the exception and report
@ -443,7 +441,7 @@ class CodeGenerator extends Object
_copyAndFlattenBlocks(items, moduleItems);
// Build the module.
return JS.Program(items, name: _buildUnit.name);
return JS.Program(items, name: options.moduleName);
}
void _emitDebuggerExtensionInfo(String name) {

View file

@ -12,7 +12,7 @@ import 'package:args/command_runner.dart' show UsageException;
import 'package:path/path.dart' as path;
import 'context.dart' show AnalyzerOptions;
import 'module_compiler.dart' show BuildUnit, CompilerOptions, ModuleCompiler;
import 'module_compiler.dart' show CompilerOptions, ModuleCompiler;
const _binaryName = 'dartdevc';
@ -103,12 +103,7 @@ ArgParser ddcArgParser({bool hide = true}) {
help: 'Ignore unrecognized command line flags.',
defaultsTo: false,
hide: hide)
..addMultiOption('out', abbr: 'o', help: 'Output file (required).')
..addOption('module-name',
help: 'The output module name, used in some JS module formats.\n'
'Defaults to the output file name (without .js).')
..addOption('library-root',
help: 'Root of source files. Library names are relative to this root.');
..addMultiOption('out', abbr: 'o', help: 'Output file (required).');
CompilerOptions.addArguments(argParser, hide: hide);
defineAnalysisArguments(argParser, hide: hide, ddc: true);
AnalyzerOptions.addArguments(argParser, hide: hide);
@ -143,32 +138,7 @@ void _compile(ArgResults argResults, AnalyzerOptions analyzerOptions,
'');
}
// TODO(jmesserly): for now the first one is special. This will go away once
// we've removed the "root" and "module name" variables.
var firstOutPath = outPaths[0];
var libraryRoot = argResults['library-root'] as String;
if (libraryRoot != null) {
libraryRoot = path.absolute(libraryRoot);
} else {
libraryRoot = Directory.current.path;
}
var moduleName = argResults['module-name'] as String;
if (moduleName == null) {
var moduleRoot = compilerOpts.moduleRoot;
if (moduleRoot != null) {
// TODO(jmesserly): remove this legacy support after a deprecation period.
// (Mainly this is to give time for migrating build rules.)
moduleName =
path.withoutExtension(path.relative(firstOutPath, from: moduleRoot));
} else {
moduleName = path.basenameWithoutExtension(firstOutPath);
}
}
var unit = BuildUnit(moduleName, libraryRoot, argResults.rest);
var module = compiler.compile(unit, compilerOpts);
var module = compiler.compile(argResults.rest, compilerOpts);
module.errors.forEach(printFn);
if (!module.isValid) {

View file

@ -133,14 +133,14 @@ class ModuleCompiler {
/// *Warning* - this may require resolving the entire world.
/// If that is not desired, the analysis context must be pre-configured using
/// summaries before calling this method.
JSModuleFile compile(BuildUnit unit, CompilerOptions options) {
JSModuleFile compile(List<String> sourcePaths, CompilerOptions options) {
var trees = <CompilationUnit>[];
var errors = <AnalysisError>[];
var librariesToCompile = Queue<LibraryElement>();
var compilingSdk = false;
for (var sourcePath in unit.sources) {
for (var sourcePath in sourcePaths) {
var sourceUri = sourcePathToUri(sourcePath);
if (sourceUri.scheme == "dart") {
compilingSdk = true;
@ -201,9 +201,9 @@ class ModuleCompiler {
}
}
var codeGenerator =
var compiler =
CodeGenerator(context, summaryData, options, _extensionTypes, errors);
return codeGenerator.compile(unit, trees);
return compiler.compile(trees);
}
Iterable<AnalysisError> _filterJsErrors(
@ -256,6 +256,10 @@ class CompilerOptions extends SharedCompilerOptions {
/// [summaryModules].
final String moduleRoot;
/// *deprecated* If specified, `dartdevc` will synthesize library names that
/// are relative to this path for all libraries in the JS module.
final String libraryRoot;
CompilerOptions(
{bool sourceMap = true,
this.sourceMapComment = true,
@ -269,7 +273,8 @@ class CompilerOptions extends SharedCompilerOptions {
Map<String, String> bazelMapping = const {},
this.summaryOutPath,
Map<String, String> summaryModules = const {},
this.moduleRoot})
this.moduleRoot,
this.libraryRoot})
: super(
sourceMap: sourceMap,
summarizeApi: summarizeApi,
@ -286,6 +291,7 @@ class CompilerOptions extends SharedCompilerOptions {
unsafeForceCompile = args['unsafe-force-compile'] as bool,
summaryOutPath = args['summary-out'] as String,
moduleRoot = args['module-root'] as String,
libraryRoot = _getLibraryRoot(args),
super.fromArguments(args, args['module-root'] as String,
args['summary-extension'] as String);
@ -312,27 +318,15 @@ class CompilerOptions extends SharedCompilerOptions {
..addOption('module-root',
help: '(deprecated) used to determine the default module name and\n'
'summary import name if those are not provided.',
hide: hide);
hide: hide)
..addOption('library-root',
help: '(deprecated) used to name libraries inside the module.');
}
}
/// A unit of Dart code that can be built into a single JavaScript module.
class BuildUnit {
/// The name of this module.
final String name;
/// All library names are relative to this path/prefix.
final String libraryRoot;
/// The list of sources in this module.
///
/// The set of Dart files can be arbitrarily large, but it must contain
/// complete libraries including all of their parts, as well as all libraries
/// that are part of a library cycle.
final List<String> sources;
BuildUnit(String modulePath, this.libraryRoot, this.sources)
: name = '${path.toUri(modulePath)}';
static String _getLibraryRoot(ArgResults args) {
var root = args['library-root'] as String;
return root != null ? path.absolute(root) : path.current;
}
}
/// The output of Dart->JS compilation.

View file

@ -41,14 +41,11 @@ ModuleFormat parseModuleFormat(String s) => {
}[s];
/// Parse the module format option added by [addModuleFormatOptions].
List<ModuleFormat> parseModuleFormatOption(ArgResults argResults) {
var format = argResults['modules'];
if (format is String) {
return [parseModuleFormat(format)];
}
var formats = (format as List<String>).map(parseModuleFormat).toList();
List<ModuleFormat> parseModuleFormatOption(ArgResults args) {
var formats =
(args['modules'] as List<String>).map(parseModuleFormat).toList();
if (argResults['single-out-file'] as bool) {
if (args['single-out-file'] as bool) {
for (int i = 0; i < formats.length; i++) {
var format = formats[i];
switch (formats[i]) {
@ -72,8 +69,7 @@ List<ModuleFormat> parseModuleFormatOption(ArgResults argResults) {
/// Adds an option to the [argParser] for choosing the module format, optionally
/// [allowMultiple] formats to be specified, with each emitted into a separate
/// file.
void addModuleFormatOptions(ArgParser argParser,
{bool allowMultiple = false, bool hide = true}) {
void addModuleFormatOptions(ArgParser argParser, {bool hide = true}) {
argParser.addMultiOption('modules', help: 'module pattern to emit', allowed: [
'es6',
'common',

View file

@ -83,6 +83,13 @@ class SharedCompilerOptions {
final List<ModuleFormat> moduleFormats;
/// The name of the module.
///
/// This used when to support file concatenation. The JS module will contain
/// its module name inside itself, allowing it to declare the module name
/// independently of the file.
String moduleName;
SharedCompilerOptions(
{this.sourceMap = true,
this.summarizeApi = true,
@ -91,7 +98,8 @@ class SharedCompilerOptions {
this.replCompile = false,
this.bazelMapping = const {},
this.summaryModules = const {},
this.moduleFormats = const []});
this.moduleFormats = const [],
this.moduleName});
SharedCompilerOptions.fromArguments(ArgResults args,
[String moduleRoot, String summaryExtension])
@ -104,10 +112,11 @@ class SharedCompilerOptions {
_parseBazelMappings(args['bazel-mapping'] as List<String>),
summaryModules: _parseCustomSummaryModules(
args['summary'] as List<String>, moduleRoot, summaryExtension),
moduleFormats: parseModuleFormatOption(args));
moduleFormats: parseModuleFormatOption(args),
moduleName: _getModuleName(args, moduleRoot));
static void addArguments(ArgParser parser, {bool hide = true}) {
addModuleFormatOptions(parser, allowMultiple: true, hide: hide);
addModuleFormatOptions(parser, hide: hide);
parser
..addMultiOption('summary',
@ -122,6 +131,9 @@ class SharedCompilerOptions {
help: 'emit metadata annotations queriable via mirrors', hide: hide)
..addFlag('enable-asserts',
help: 'enable assertions', defaultsTo: true, hide: hide)
..addOption('module-name',
help: 'The output module name, used in some JS module formats.\n'
'Defaults to the output file name (without .js).')
// TODO(jmesserly): rename this, it has nothing to do with bazel.
..addMultiOption('bazel-mapping',
help: '--bazel-mapping=gen/to/library.dart,to/library.dart\n'
@ -129,6 +141,32 @@ class SharedCompilerOptions {
splitCommas: false,
hide: hide);
}
static String _getModuleName(ArgResults args, String moduleRoot) {
var moduleName = args['module-name'] as String;
if (moduleName == null) {
var outPaths = args['out'];
var outPath = outPaths is String
? outPaths
: (outPaths as List<String>)
.firstWhere((_) => true, orElse: () => null);
if (moduleRoot != null) {
// TODO(jmesserly): remove this legacy support after a deprecation period.
// (Mainly this is to give time for migrating build rules.)
moduleName =
path.withoutExtension(path.relative(outPath, from: moduleRoot));
} else {
moduleName = path.basenameWithoutExtension(outPath);
}
}
// TODO(jmesserly): this should probably use sourcePathToUri.
//
// Also we should not need this logic if the user passed in the module name
// explicitly. It is here for backwards compatibility until we can confirm
// that build systems do not depend on passing windows-style paths here.
return path.toUri(moduleName).toString();
}
}
/// Finds explicit module names of the form `path=name` in [summaryPaths],

View file

@ -221,6 +221,11 @@ Future<CompilerResult> _compile(List<String> args,
var jsModule =
compiler.emitModule(component, result.inputSummaries, summaryModules);
// TODO(jmesserly): support for multiple output formats?
//
// Also the old Analyzer backend had some code to make debugging better when
// --single-out-file is used, but that option does not appear to be used by
// any of our build systems.
var jsCode = jsProgramToCode(jsModule, options.moduleFormats.first,
buildSourceMap: argResults['source-map'] as bool,
jsUrl: path.toUri(output).toString(),

View file

@ -234,12 +234,12 @@ class ProgramCompiler extends Object
bool get emitMetadata => options.emitMetadata;
JS.Program emitModule(Component buildUnit, List<Component> summaries,
JS.Program emitModule(Component component, List<Component> summaries,
Map<Uri, String> summaryModules) {
if (moduleItems.isNotEmpty) {
throw StateError('Can only call emitModule once.');
}
_component = buildUnit;
_component = component;
var moduleImports = summaryModules.values.toList();
for (var i = 0; i < summaries.length; i++) {
@ -252,7 +252,7 @@ class ProgramCompiler extends Object
}
}
var libraries = buildUnit.libraries.where((l) => !l.isExternal);
var libraries = component.libraries.where((l) => !l.isExternal);
var ddcRuntime =
libraries.firstWhere(isSdkInternalRuntime, orElse: () => null);
if (ddcRuntime != null) {
@ -335,7 +335,7 @@ class ProgramCompiler extends Object
_copyAndFlattenBlocks(items, moduleItems);
// Build the module.
return JS.Program(items, name: buildUnit.root.name);
return JS.Program(items, name: options.moduleName);
}
/// Flattens blocks in [items] to a single list.

View file

@ -52,9 +52,11 @@ Future main(List<String> args) async {
await Directory(outputDir).create(recursive: true);
await writeComponentToBinary(component, outputPath);
var jsModule =
ProgramCompiler(component, target.hierarchy, SharedCompilerOptions(), {})
.emitModule(component, [], {});
var jsModule = ProgramCompiler(
component,
target.hierarchy,
SharedCompilerOptions(moduleName: 'dart_sdk'),
{}).emitModule(component, [], {});
var moduleFormats = {
'amd': ModuleFormat.amd,
'common': ModuleFormat.common,

View file

@ -293,15 +293,14 @@ class WebCompileCommand extends Command {
}
resources.newFile(fileName, sourceCode);
var unit = BuildUnit(libraryName, "", [fileName]);
JSModuleFile module = compiler.compile(unit, compilerOptions);
compilerOptions.moduleName = path.toUri(libraryName).toString();
JSModuleFile module = compiler.compile([fileName], compilerOptions);
var moduleCode = '';
if (module.isValid) {
moduleCode = module
.getCode(ModuleFormat.legacyConcat, unit.name, unit.name + '.map')
.code;
var name = compilerOptions.moduleName;
moduleCode =
module.getCode(ModuleFormat.legacyConcat, name, name + '.map').code;
}
return CompileResult(