807660f323
This is a step towards organizing the code from shared_command.dart and eventually deleting it. - Move `SharedCompilerOptions` and rename to `Options` from shared_command.dart along with helpers. Change-Id: Ic9edfb47fc2425a28b1e948d94fad96a2f594108 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388048 Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Mark Zhou <markzipan@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com> Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
364 lines
14 KiB
Dart
364 lines
14 KiB
Dart
// Copyright (c) 2024, 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 'package:args/args.dart';
|
|
|
|
import 'package:front_end/src/api_unstable/ddc.dart'
|
|
show parseExperimentalArguments;
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import '../compiler/module_builder.dart';
|
|
|
|
/// Compiler options for the `dartdevc` backend.
|
|
class Options {
|
|
/// Whether to emit the source mapping file.
|
|
///
|
|
/// This supports debugging the original source code instead of the generated
|
|
/// code.
|
|
final bool sourceMap;
|
|
|
|
/// Whether to emit the source mapping file in the program text, so the
|
|
/// runtime can enable synchronous stack trace deobfuscation.
|
|
final bool inlineSourceMap;
|
|
|
|
/// Whether to emit the full compiled kernel.
|
|
///
|
|
/// This is used by expression compiler worker, launched from the debugger
|
|
/// in webdev and google3 scenarios, for expression evaluation features.
|
|
/// Full kernel for compiled files is needed to be able to compile
|
|
/// expressions on demand in the current scope of a breakpoint.
|
|
final bool emitFullCompiledKernel;
|
|
|
|
/// Whether to emit a summary file containing API signatures.
|
|
///
|
|
/// This is required for a modular build process.
|
|
final bool summarizeApi;
|
|
|
|
// Whether to enable assertions.
|
|
final bool enableAsserts;
|
|
|
|
/// Whether to compile code in a more permissive REPL mode allowing access
|
|
/// to private members across library boundaries.
|
|
///
|
|
/// This should only set `true` by our REPL compiler.
|
|
bool replCompile;
|
|
|
|
/// Whether to emit the debug metadata
|
|
///
|
|
/// Debugger uses this information about to construct mapping between
|
|
/// modules and libraries that otherwise requires expensive communication with
|
|
/// the browser.
|
|
final bool emitDebugMetadata;
|
|
|
|
/// Whether to emit the debug symbols
|
|
///
|
|
/// Debugger uses this information about to construct mapping between
|
|
/// dart and js objects that otherwise requires expensive communication with
|
|
/// the browser.
|
|
final bool emitDebugSymbols;
|
|
|
|
final Map<String, String> summaryModules;
|
|
|
|
final List<ModuleFormat> moduleFormats;
|
|
|
|
/// The name of the module.
|
|
///
|
|
/// This is used 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.
|
|
final String moduleName;
|
|
|
|
/// Custom scheme to indicate a multi-root uri.
|
|
final String multiRootScheme;
|
|
|
|
/// Path to set multi-root files relative to when generating source-maps.
|
|
final String? multiRootOutputPath;
|
|
|
|
/// Experimental language features that are enabled/disabled, see
|
|
/// [the spec](https://github.com/dart-lang/sdk/blob/master/docs/process/experimental-flags.md)
|
|
/// for more details.
|
|
final Map<String, bool> experiments;
|
|
|
|
final bool soundNullSafety;
|
|
|
|
/// Whether or not the `--canary` flag was specified during compilation.
|
|
final bool canaryFeatures;
|
|
|
|
/// When `true` the [Component] will be compiled into a format compatible with
|
|
/// hot reload.
|
|
///
|
|
/// The output will still be a single file containing each library in an
|
|
/// isolated namespace.
|
|
final bool emitLibraryBundle;
|
|
|
|
/// Whether the compiler is generating a dynamic module.
|
|
final bool dynamicModule;
|
|
|
|
/// When `true` stars "*" will appear to represent legacy types when printing
|
|
/// runtime types in the compiled application.
|
|
final bool printLegacyStars = false;
|
|
|
|
/// Raw precompiled macro options, each of the format
|
|
/// `<program-uri>;<macro-library-uri>`.
|
|
///
|
|
/// Multiple library URIs may be provided separated by additional semicolons.
|
|
final List<String> precompiledMacros;
|
|
|
|
/// The serialization mode to use for macro communication.
|
|
final String? macroSerializationMode;
|
|
|
|
Options(
|
|
{this.sourceMap = true,
|
|
this.inlineSourceMap = false,
|
|
this.summarizeApi = true,
|
|
this.enableAsserts = true,
|
|
this.replCompile = false,
|
|
this.emitDebugMetadata = false,
|
|
this.emitDebugSymbols = false,
|
|
this.emitFullCompiledKernel = false,
|
|
this.summaryModules = const {},
|
|
this.moduleFormats = const [],
|
|
required this.moduleName,
|
|
this.multiRootScheme = 'org-dartlang-app',
|
|
this.multiRootOutputPath,
|
|
this.experiments = const {},
|
|
this.soundNullSafety = true,
|
|
this.canaryFeatures = false,
|
|
this.dynamicModule = false,
|
|
this.precompiledMacros = const [],
|
|
this.macroSerializationMode})
|
|
: emitLibraryBundle = canaryFeatures &&
|
|
moduleFormats.length == 1 &&
|
|
moduleFormats.single == ModuleFormat.ddc;
|
|
|
|
Options.fromArguments(ArgResults args)
|
|
: this(
|
|
sourceMap: args['source-map'] as bool,
|
|
inlineSourceMap: args['inline-source-map'] as bool,
|
|
summarizeApi: args['summarize'] as bool,
|
|
enableAsserts: args['enable-asserts'] as bool,
|
|
replCompile: args['repl-compile'] as bool,
|
|
emitDebugMetadata: args['experimental-emit-debug-metadata'] as bool,
|
|
emitDebugSymbols: args['emit-debug-symbols'] as bool,
|
|
emitFullCompiledKernel:
|
|
args['experimental-output-compiled-kernel'] as bool,
|
|
summaryModules:
|
|
_parseCustomSummaryModules(args['summary'] as List<String>),
|
|
moduleFormats: parseModuleFormatOption(args),
|
|
moduleName: _getModuleName(args),
|
|
multiRootScheme: args['multi-root-scheme'] as String,
|
|
multiRootOutputPath: args['multi-root-output-path'] as String?,
|
|
experiments: parseExperimentalArguments(
|
|
args['enable-experiment'] as List<String>),
|
|
soundNullSafety: args['sound-null-safety'] as bool,
|
|
canaryFeatures: args['canary'] as bool,
|
|
dynamicModule: args['dynamic-module'] as bool,
|
|
precompiledMacros: args['precompiled-macro'] as List<String>,
|
|
macroSerializationMode:
|
|
args['macro-serialization-mode'] as String?);
|
|
|
|
Options.fromSdkRequiredArguments(ArgResults args)
|
|
: this(
|
|
summarizeApi: false,
|
|
moduleFormats: parseModuleFormatOption(args),
|
|
// When compiling the SDK use dart_sdk as the default. This is the
|
|
// assumed name in various places around the build systems.
|
|
moduleName:
|
|
args['module-name'] != null ? _getModuleName(args) : 'dart_sdk',
|
|
multiRootScheme: args['multi-root-scheme'] as String,
|
|
multiRootOutputPath: args['multi-root-output-path'] as String?,
|
|
experiments: parseExperimentalArguments(
|
|
args['enable-experiment'] as List<String>),
|
|
soundNullSafety: args['sound-null-safety'] as bool,
|
|
canaryFeatures: args['canary'] as bool);
|
|
|
|
static void addArguments(ArgParser parser, {bool hide = true}) {
|
|
addSdkRequiredArguments(parser, hide: hide);
|
|
|
|
parser
|
|
..addMultiOption('summary',
|
|
abbr: 's',
|
|
help: 'API summary file(s) of imported libraries, optionally\n'
|
|
'with module import path: -s path.dill=js/import/path')
|
|
..addFlag('summarize',
|
|
help: 'Emit an API summary file.', defaultsTo: true, hide: hide)
|
|
..addFlag('source-map',
|
|
help: 'Emit source mapping.', defaultsTo: true, hide: hide)
|
|
..addFlag('inline-source-map',
|
|
help: 'Emit source mapping inline.', defaultsTo: false, hide: hide)
|
|
..addFlag('enable-asserts',
|
|
help: 'Enable assertions.', defaultsTo: true, hide: hide)
|
|
..addFlag('repl-compile',
|
|
help: 'Compile in a more permissive REPL mode, allowing access'
|
|
' to private members across library boundaries. This should'
|
|
' only be used by debugging tools.',
|
|
defaultsTo: false,
|
|
hide: hide)
|
|
// TODO(41852) Define a process for breaking changes before graduating from
|
|
// experimental.
|
|
..addFlag('experimental-emit-debug-metadata',
|
|
help: 'Experimental option for compiler development.\n'
|
|
'Output a metadata file for debug tools next to the .js output.',
|
|
defaultsTo: false,
|
|
hide: true)
|
|
..addFlag('emit-debug-symbols',
|
|
help: 'Experimental option for compiler development.\n'
|
|
'Output a symbols file for debug tools next to the .js output.',
|
|
defaultsTo: false,
|
|
hide: true)
|
|
..addFlag('experimental-output-compiled-kernel',
|
|
help: 'Experimental option for compiler development.\n'
|
|
'Output a full kernel file for currently compiled module next to '
|
|
'the .js output.',
|
|
defaultsTo: false,
|
|
hide: true)
|
|
..addMultiOption('precompiled-macro',
|
|
help:
|
|
'Configuration for precompiled macro binaries or kernel files.\n'
|
|
'The expected format of this option is as follows: '
|
|
'<absolute-path-to-binary>;<macro-library-uri>\nFor example: '
|
|
'--precompiled-macro="/path/to/compiled/macro;'
|
|
'package:some_macro/some_macro.dart". Multiple library uris may be '
|
|
'passed as well (separated by semicolons).',
|
|
hide: true)
|
|
..addOption('macro-serialization-mode',
|
|
help: 'The serialization mode for communicating with macros.',
|
|
allowed: ['bytedata', 'json'],
|
|
defaultsTo: 'bytedata')
|
|
..addFlag('dynamic-module',
|
|
help: 'Compile to generate a dynamic module',
|
|
negatable: false,
|
|
defaultsTo: false);
|
|
}
|
|
|
|
/// Adds only the arguments used to compile the SDK from a full dill file.
|
|
///
|
|
/// NOTE: The 'module-name' option will have a special default value of
|
|
/// 'dart_sdk' when compiling the SDK.
|
|
/// See [SharedOptions.fromSdkRequiredArguments].
|
|
static void addSdkRequiredArguments(ArgParser parser, {bool hide = true}) {
|
|
addModuleFormatOptions(parser, hide: hide);
|
|
parser
|
|
..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('multi-root-scheme',
|
|
help: 'The custom scheme to indicate a multi-root uri.',
|
|
defaultsTo: 'org-dartlang-app')
|
|
..addOption('multi-root-output-path',
|
|
help: 'Path to set multi-root files relative to when generating'
|
|
' source-maps.',
|
|
hide: true)
|
|
..addMultiOption('enable-experiment',
|
|
help: 'Enable/disable experimental language features.', hide: hide)
|
|
..addFlag('sound-null-safety',
|
|
help: 'Compile for sound null safety at runtime.',
|
|
negatable: true,
|
|
defaultsTo: true)
|
|
..addFlag('canary',
|
|
help: 'Enable all compiler features under active development. '
|
|
'This option is intended for compiler development only. '
|
|
'Canary features are likely to be unstable and can be removed '
|
|
'without warning.',
|
|
defaultsTo: false,
|
|
hide: true);
|
|
}
|
|
|
|
static String _getModuleName(ArgResults args) {
|
|
var moduleName = args['module-name'] as String?;
|
|
if (moduleName == null) {
|
|
var outPaths = args['out'] as List<String>;
|
|
if (outPaths.isEmpty) {
|
|
throw UnsupportedError(
|
|
'No module name provided and unable to synthesize one without any '
|
|
'output paths.');
|
|
}
|
|
var outPath = outPaths.first;
|
|
moduleName = p.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 p.toUri(moduleName).toString();
|
|
}
|
|
}
|
|
|
|
/// Finds explicit module names of the form `path=name` in [summaryPaths],
|
|
/// and returns the path to mapping in an ordered map from `path` to `name`.
|
|
///
|
|
/// A summary path can contain "=" followed by an explicit module name to
|
|
/// allow working with summaries whose physical location is outside of the
|
|
/// module root directory.
|
|
Map<String, String> _parseCustomSummaryModules(List<String> summaryPaths,
|
|
[String? moduleRoot, String? summaryExt]) {
|
|
var pathToModule = <String, String>{};
|
|
for (var summaryPath in summaryPaths) {
|
|
var equalSign = summaryPath.indexOf('=');
|
|
String modulePath;
|
|
var summaryPathWithoutExt = summaryExt != null
|
|
? summaryPath.substring(
|
|
0,
|
|
// Strip off the extension, including the last `.`.
|
|
summaryPath.length - (summaryExt.length + 1))
|
|
: p.withoutExtension(summaryPath);
|
|
if (equalSign != -1) {
|
|
modulePath = summaryPath.substring(equalSign + 1);
|
|
summaryPath = summaryPath.substring(0, equalSign);
|
|
} else if (moduleRoot != null && p.isWithin(moduleRoot, summaryPath)) {
|
|
// TODO: Determine if this logic is still needed.
|
|
modulePath = p.url.joinAll(
|
|
p.split(p.relative(summaryPathWithoutExt, from: moduleRoot)));
|
|
} else {
|
|
modulePath = p.basename(summaryPathWithoutExt);
|
|
}
|
|
pathToModule[summaryPath] = modulePath;
|
|
}
|
|
return pathToModule;
|
|
}
|
|
|
|
/// Taken from analyzer to implement `--ignore-unrecognized-flags`
|
|
List<String> filterUnknownArguments(List<String> args, ArgParser parser) {
|
|
if (!args.contains('--ignore-unrecognized-flags')) return args;
|
|
|
|
var knownOptions = <String>{};
|
|
var knownAbbreviations = <String>{};
|
|
parser.options.forEach((String name, Option option) {
|
|
knownOptions.add(name);
|
|
var abbreviation = option.abbr;
|
|
if (abbreviation != null) {
|
|
knownAbbreviations.add(abbreviation);
|
|
}
|
|
if (option.negatable != null && option.negatable!) {
|
|
knownOptions.add('no-$name');
|
|
}
|
|
});
|
|
|
|
String optionName(int prefixLength, String arg) {
|
|
var equalsOffset = arg.lastIndexOf('=');
|
|
if (equalsOffset < 0) {
|
|
return arg.substring(prefixLength);
|
|
}
|
|
return arg.substring(prefixLength, equalsOffset);
|
|
}
|
|
|
|
var filtered = <String>[];
|
|
for (var arg in args) {
|
|
if (arg.startsWith('--') && arg.length > 2) {
|
|
if (knownOptions.contains(optionName(2, arg))) {
|
|
filtered.add(arg);
|
|
}
|
|
} else if (arg.startsWith('-') && arg.length > 1) {
|
|
if (knownAbbreviations.contains(optionName(1, arg))) {
|
|
filtered.add(arg);
|
|
}
|
|
} else {
|
|
filtered.add(arg);
|
|
}
|
|
}
|
|
return filtered;
|
|
}
|