Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dart:io] Adds Platform.architecture #56959

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/dart2native/lib/dart2native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Future<ProcessResult> generateKernelHelper({
String? packages,
List<String> defines = const [],
String enableExperiment = '',
String? targetArch,
String? targetOS,
List<String> extraGenKernelOptions = const [],
String? nativeAssets,
Expand All @@ -129,6 +130,7 @@ Future<ProcessResult> generateKernelHelper({
'--platform=${product ? productPlatformDill : platformDill}',
if (product) '-Ddart.vm.product=true',
if (enableExperiment.isNotEmpty) '--enable-experiment=$enableExperiment',
if (targetArch != null) '--target-arch=$targetArch',
if (targetOS != null) '--target-os=$targetOS',
if (fromDill) '--from-dill=$sourceFile',
if (aot) '--aot',
Expand Down
23 changes: 23 additions & 0 deletions pkg/dart2native/lib/generate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extension type KernelGenerator._(_Generator _generator) {
String? outputFile,
String? debugFile,
String? packages,
String? targetArch,
String? targetOS,
String? depFile,
String enableExperiment = '',
Expand All @@ -57,6 +58,7 @@ extension type KernelGenerator._(_Generator _generator) {
kind: kind,
outputFile: outputFile,
packages: packages,
targetArch: targetArch,
targetOS: targetOS,
verbose: verbose,
verbosity: verbosity,
Expand Down Expand Up @@ -120,6 +122,12 @@ class _Generator {
/// Specifies the file debugging information should be written to.
final String? _debugFile;

/// Specifies the CPU architecture the executable is being generated for.
///
/// This must be provided when [_kind] is [Kind.exe], and it must match the current
/// CPU architecture.
final String? _targetArch;

/// Specifies the operating system the executable is being generated for. This
/// must be provided when [_kind] is [Kind.exe], and it must match the current
/// operating system.
Expand Down Expand Up @@ -160,6 +168,7 @@ class _Generator {
String? outputFile,
String? debugFile,
String? packages,
String? targetArch,
String? targetOS,
String? depFile,
required String enableExperiment,
Expand All @@ -173,6 +182,7 @@ class _Generator {
_verbosity = verbosity,
_enableAsserts = enableAsserts,
_enableExperiment = enableExperiment,
_targetArch = targetArch,
_targetOS = targetOS,
_debugFile = debugFile,
_outputFile = outputFile,
Expand All @@ -182,6 +192,13 @@ class _Generator {
_sourcePath = _normalize(sourceFile)!,
_packages = _normalize(packages) {
if (_kind == Kind.exe) {
if (_targetArch == null) {
throw ArgumentError('targetArch must be specified for executables.');
Copy link

@alestiago alestiago Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the Flutter team once went over the errors the tooling could throw and added clear explanation points on how to move forward with the aim of improving the developer experience. I would expect such practice to also be present here.

If I encounter "targetArch must be specified for executables" I would ask myself:

  • "what is targetArch", is it an abbreviation for "target architecture"? (see Effective Dart for abbreviations)
  • How can I specify so? Is it a parameter option --targetArch? Is there an example on how to specify it?
  • What are the available architectures I can compile into? Is there documentation stating all the supported architectures? Maybe we can point the reader to: https://github.com/dart-lang/sdk/blob/main/docs/Supported-Architectures.md? Note that these are already in the "help command with TargetArch.names, but it is not as clear as the table provided in the documentation.
  • What architecture is my system running? Could the message include a suggested resolution with the current architecture my system is using?

For a great developer experience, I would hope all the above questions are answered within the error message I encounter.

For example, a new improved message would be (ignore bad formatting and bad command):

ArgumentError('''
The target architecture for the Dart executable must be specified and it was not. Make sure you specify
the `--targetArch` option to the compile command:

dart compile exe bin/foo.dart --target-os macos --target-arch armv8

Refer to [supported architectures](https://github.com/dart-lang/sdk/blob/main/docs/Supported-Architectures.md) 
for a comprehensive table of all the available architectures per operating system.
''');

I would also like to get a message on how to request support for an architecture that is not already supported, so if the input is for an unsupported os-architecture combination point me to the repository where I could open an issue to request support or contribute back.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"what is targetArch", is it an abbreviation for "target architecture"?

Yes, it is an abbreviation for "target architecture". I was thinking targetCPU but in my experience, that usually is for specific CPU optimizations (eg. zen1, skylake, generic, baseline).

How can I specify so? Is it a parameter option --targetArch? Is there an example on how to specify it?

--target-arch was added to pair with --target-os. However, --target-os doesn't support cross compiling and so I haven't been able to get --target-arch to cross compile. I at least added the logic for --target-arch to pair with the existing --target-os flag.

What are the available architectures I can compile into? Is there documentation stating all the supported architectures? Maybe we can point the reader to: https://github.com/dart-lang/sdk/blob/main/docs/Supported-Architectures.md? Note that these are already in the "help command with TargetArch.names, but it is not as clear as the table provided in the documentation.

The TargetArch.names I added in pkg/vm/lib/target_arch.dart was based on that table + observations I saw when using grep to figure out how this repo works.

What architecture is my system running? Could the message include a suggested resolution with the current architecture my system is using?

Could you provide more information to what this sort of means?

For example, a new improved message would be (ignore bad formatting and bad command

Should we also change the one for the target OS?

} else if (_targetArch != Platform.architecture) {
throw UnsupportedError(
'Cross compilation not supported for executables.');
}

if (_targetOS == null) {
throw ArgumentError('targetOS must be specified for executables.');
} else if (_targetOS != Platform.operatingSystem) {
Expand All @@ -196,6 +213,10 @@ class _Generator {
List<String>? extraOptions,
}) async {
if (_verbose) {
if (_targetArch != null) {
print('Specializing Platform getters for target arch $_targetArch.');
}

if (_targetOS != null) {
print('Specializing Platform getters for target OS $_targetOS.');
}
Expand All @@ -212,6 +233,7 @@ class _Generator {
fromDill: await isKernelFile(_sourcePath),
enableAsserts: _enableAsserts,
enableExperiment: _enableExperiment,
targetArch: _targetArch,
targetOS: _targetOS,
extraGenKernelOptions: [
'--invocation-modes=compile',
Expand Down Expand Up @@ -308,6 +330,7 @@ class _Generator {
defines: _defines,
enableAsserts: _enableAsserts,
enableExperiment: _enableExperiment,
targetArch: _targetArch,
targetOS: _targetOS,
extraGenKernelOptions: [
'--invocation-modes=compile',
Expand Down
21 changes: 21 additions & 0 deletions pkg/dartdev/lib/src/commands/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:front_end/src/api_prototype/compiler_options.dart'
import 'package:native_assets_builder/native_assets_builder.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:path/path.dart' as path;
import 'package:vm/target_arch.dart'; // For possible --target-arch values.
import 'package:vm/target_os.dart'; // For possible --target-os values.

import '../core.dart';
Expand Down Expand Up @@ -48,6 +49,9 @@ class BuildCommand extends DartdevCommand {
allowed: ['exe', 'aot'],
defaultsTo: 'exe',
)
..addOption('target-arch',
help: 'Compile to a specific target CPU architecture.',
allowed: TargetArch.names)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
Expand Down Expand Up @@ -108,6 +112,22 @@ class BuildCommand extends DartdevCommand {
sourceUri.pathSegments.last.split('.').first,
),
);
String? targetArch = args['target-arch'];
if (format != Kind.exe) {
assert(format == Kind.aot);
// If we're generating an AOT snapshot and not an executable, then
// targetArch is allowed to be null for a platform-independent snapshot
// or a different platform than the host.
} else if (targetArch == null) {
targetArch = Platform.architecture;
} else if (targetArch != Platform.architecture) {
stderr.writeln(
"'dart build -f ${format.name}' does not support cross-arch compilation.");
stderr.writeln('Host arch: ${Platform.architecture}');
stderr.writeln('Target arch: $targetArch');
return 128;
}

String? targetOS = args['target-os'];
if (format != Kind.exe) {
assert(format == Kind.aot);
Expand Down Expand Up @@ -181,6 +201,7 @@ class BuildCommand extends DartdevCommand {
verbosity: args.option('verbosity')!,
defines: [],
packages: packageConfig?.toFilePath(),
targetArch: targetArch,
targetOS: targetOS,
enableExperiment: args.enabledExperiments.join(','),
tempDir: tempDir,
Expand Down
22 changes: 22 additions & 0 deletions pkg/dartdev/lib/src/commands/compile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:dart2native/generate.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show Verbosity;
import 'package:path/path.dart' as path;
import 'package:vm/target_arch.dart'; // For possible --target-arch values.
import 'package:vm/target_os.dart'; // For possible --target-os values.

import '../core.dart';
Expand Down Expand Up @@ -455,6 +456,9 @@ Remove debugging information from the output and save it separately to the speci
hide: true,
valueHelp: 'opt1,opt2,...',
)
..addOption('target-arch',
help: 'Compile to a specific target CPU architecture.',
allowed: TargetArch.names)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
Expand Down Expand Up @@ -512,6 +516,22 @@ Remove debugging information from the output and save it separately to the speci
}
}

String? targetArch = args.option('target-arch');
if (format != Kind.exe) {
assert(format == Kind.aot);
// If we're generating an AOT snapshot and not an executable, then
// targetOS is allowed to be null for a platform-independent snapshot
// or a different platform than the host.
} else if (targetArch == null) {
targetArch = Platform.architecture;
} else if (targetArch != Platform.architecture) {
stderr.writeln(
"'dart compile $commandName' does not support cross-arch compilation.");
stderr.writeln('Host arch: ${Platform.architecture}');
stderr.writeln('Target arch: $targetArch');
return 128;
}

String? targetOS = args.option('target-os');
if (format != Kind.exe) {
assert(format == Kind.aot);
Expand All @@ -527,6 +547,7 @@ Remove debugging information from the output and save it separately to the speci
stderr.writeln('Target OS: $targetOS');
return 128;
}

final tempDir = Directory.systemTemp.createTempSync();
try {
final kernelGenerator = KernelGenerator(
Expand All @@ -540,6 +561,7 @@ Remove debugging information from the output and save it separately to the speci
debugFile: args.option('save-debugging-info'),
verbose: verbose,
verbosity: args.option('verbosity')!,
targetArch: targetArch,
targetOS: targetOS,
tempDir: tempDir,
depFile: args.option('depfile'),
Expand Down
6 changes: 6 additions & 0 deletions pkg/dartdev/test/commands/compile_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,16 @@ void defineCompileTests() {
mainSrc: 'void main() {print(const String.fromEnvironment("cross"));}');
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
final outFile = path.canonicalize(path.join(p.dirPath, 'myexe'));
final targetArch = Platform.architecture;
final targetOS = Platform.isLinux ? 'macos' : 'linux';

final result = await p.run(
[
'compile',
'exe',
'-v',
'--target-arch',
targetArch,
'--target-os',
targetOS,
'-o',
Expand Down Expand Up @@ -474,6 +477,7 @@ void defineCompileTests() {
}, skip: isRunningOnIA32);

test('Compile aot snapshot can compile to host platform', () async {
final targetArch = Platform.architecture;
final targetOS = Platform.operatingSystem;
final p = project(mainSrc: 'void main() { print("I love $targetOS"); }');
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
Expand All @@ -484,6 +488,8 @@ void defineCompileTests() {
'compile',
'aot-snapshot',
'-v',
'--target-arch',
targetArch,
'--target-os',
targetOS,
'-o',
Expand Down
12 changes: 12 additions & 0 deletions pkg/frontend_server/lib/frontend_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import 'package:kernel/target/targets.dart' show targets, TargetFlags;
import 'package:package_config/package_config.dart';
import 'package:vm/incremental_compiler.dart' show IncrementalCompiler;
import 'package:vm/kernel_front_end.dart';
import 'package:vm/target_arch.dart'; // For possible --target-arch values.
import 'package:vm/target_os.dart'; // For possible --target-os values.

import 'src/javascript_bundle.dart';
Expand All @@ -50,6 +51,9 @@ ArgParser argParser = new ArgParser(allowTrailingOptions: true)
..addFlag('aot',
help: 'Run compiler in AOT mode (enables whole-program transformations)',
defaultsTo: false)
..addOption('target-arch',
help: 'Compile to a specific target CPU architecture.',
allowed: TargetArch.names)
..addOption('target-os',
help: 'Compile to a specific target operating system.',
allowed: TargetOS.names)
Expand Down Expand Up @@ -572,6 +576,13 @@ class FrontendCompiler implements CompilerInterface {
}
}

if (options['target-arch'] != null) {
if (!options['aot']) {
print('Error: --target-arch option must be used with --aot');
return false;
}
}

if (options['target-os'] != null) {
if (!options['aot']) {
print('Error: --target-os option must be used with --aot');
Expand Down Expand Up @@ -673,6 +684,7 @@ class FrontendCompiler implements CompilerInterface {
options['keep-class-names-implementing'],
dynamicInterface: dynamicInterfaceUri,
aot: options['aot'],
targetArch: options['target-arch'],
targetOS: options['target-os'],
useGlobalTypeFlowAnalysis: options['tfa'],
useRapidTypeAnalysis: options['rta'],
Expand Down
12 changes: 11 additions & 1 deletion pkg/vm/lib/kernel_front_end.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import 'modular/target/install.dart' show installAdditionalTargets;
import 'modular/transformations/call_site_annotator.dart'
as call_site_annotator;
import 'native_assets/synthesizer.dart';
import 'target_arch.dart';
import 'target_os.dart';
import 'transformations/deferred_loading.dart' as deferred_loading;
import 'transformations/devirtualization.dart' as devirtualization
Expand Down Expand Up @@ -123,6 +124,9 @@ void declareCompilerOptions(ArgParser args) {
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: true);
args.addOption('target-arch',
help: 'Compile for a specific target CPU architecture when in AOT mode.',
allowed: TargetArch.names);
args.addOption('target-os',
help: 'Compile for a specific target operating system when in AOT mode.',
allowed: TargetOS.names);
Expand Down Expand Up @@ -222,6 +226,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
final String? depfileTarget = options['depfile-target'];
final String? fromDillFile = options['from-dill'];
final List<String>? fileSystemRoots = options['filesystem-root'];
final String? targetArch = options['target-arch'];
final String? targetOS = options['target-os'];
final bool aot = options['aot'];
final bool tfa = options['tfa'];
Expand Down Expand Up @@ -353,6 +358,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
useProtobufTreeShakerV2: useProtobufTreeShakerV2,
minimalKernel: minimalKernel,
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
targetArch: targetArch,
targetOS: targetOS,
fromDillFile: fromDillFile));

Expand Down Expand Up @@ -463,6 +469,7 @@ class KernelCompilationArguments {
final bool treeShakeWriteOnlyFields;
final bool useProtobufTreeShakerV2;
final bool minimalKernel;
final String? targetArch;
final String? targetOS;
final String? fromDillFile;

Expand All @@ -485,6 +492,7 @@ class KernelCompilationArguments {
this.treeShakeWriteOnlyFields = false,
this.useProtobufTreeShakerV2 = false,
this.minimalKernel = false,
this.targetArch,
this.targetOS,
this.fromDillFile,
}) : environmentDefines = environmentDefines ?? {};
Expand Down Expand Up @@ -625,10 +633,12 @@ Future runGlobalTransformations(Target target, Component component,

// Perform unreachable code elimination, which should be performed before
// type flow analysis so TFA won't take unreachable code into account.
final targetArch = args.targetArch;
final arch = targetArch != null ? TargetArch.fromString(targetArch)! : null;
final targetOS = args.targetOS;
final os = targetOS != null ? TargetOS.fromString(targetOS)! : null;
final evaluator = vm_constant_evaluator.VMConstantEvaluator.create(
target, component, os,
target, component, arch, os,
enableAsserts: args.enableAsserts,
environmentDefines: args.environmentDefines,
coreTypes: coreTypes);
Expand Down
27 changes: 27 additions & 0 deletions pkg/vm/lib/target_arch.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2023, 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.

enum TargetArch {
arm64('arm64'),
ia32('ia32'),
riscv32('riscv32'),
riscv64('riscv64'),
x64('x64');

final String name;

const TargetArch(this.name);

static final Iterable<String> names = values.map((v) => v.name);

static TargetArch? fromString(String s) {
for (final arch in values) {
if (arch.name == s) return arch;
}
return null;
}

@override
String toString() => name;
}
Loading