Skip to content

Commit f546362

Browse files
Clement Skaucommit-bot@chromium.org
authored andcommitted
[SDK] Adds dart2exe to create standalone executables.
Tested: ./tools/build.py --arch x64 --mode release --verbose create_sdk copy_gen_kernel_snapshot copy_dart2aot ./tools/build.py --arch x64 --mode product --verbose copy_gen_snapshot copy_dartaotruntime cp -r out/ProductX64/dart-sdk/bin/{dartaotruntime,utils/} out/ReleaseX64/dart-sdk/bin/ out/ReleaseX64/dart-sdk/bin/dart2native ~/src/hello_world.dart ~/tmp/hello_world.exe dart tools/bots/aot_smoke_tests.dart python tools/test.py -n dartkp-linux-release-x64 vm/dart/run_appended_aot_snapshot_test Change-Id: I149fcd18405cdf0a87b8f4b4072c0f0e8f98c067 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/117140 Commit-Queue: Clement Skau <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent 94dd49c commit f546362

File tree

12 files changed

+358
-236
lines changed

12 files changed

+358
-236
lines changed

.packages

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ crypto:third_party/pkg/crypto/lib
2929
csslib:third_party/pkg/csslib/lib
3030
dart2js_info:third_party/pkg/dart2js_info/lib
3131
dart2js_tools:pkg/dart2js_tools/lib
32+
dart2native:pkg/dart2native/lib
3233
dart_internal:pkg/dart_internal/lib
3334
dart_style:third_party/pkg_tested/dart_style/lib
3435
dartdoc:third_party/pkg/dartdoc/lib

pkg/dart2native/bin/dart2aot.dart

Lines changed: 0 additions & 81 deletions
This file was deleted.

pkg/dart2native/bin/dart2native.dart

Lines changed: 120 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,68 +3,140 @@
33
// for details. All rights reserved. Use of this source code is governed by a
44
// BSD-style license that can be found in the LICENSE file.
55

6+
import 'dart:io';
7+
68
import 'package:args/args.dart';
7-
import 'dart2aot.dart';
9+
import 'package:dart2native/dart2native.dart';
10+
import 'package:path/path.dart' as path;
811

9-
typedef void Command(ArgResults args, List<String> ds);
12+
final String executableSuffix = Platform.isWindows ? '.exe' : '';
13+
final String snapshotDir = path.dirname(Platform.script.toFilePath());
14+
final String binDir = path.canonicalize(path.join(snapshotDir, '..'));
15+
final String sdkDir = path.canonicalize(path.join(binDir, '..'));
16+
final String dart = path.join(binDir, 'dart${executableSuffix}');
17+
final String genKernel = path.join(snapshotDir, 'gen_kernel.dart.snapshot');
18+
final String dartaotruntime =
19+
path.join(binDir, 'dartaotruntime${executableSuffix}');
20+
final String genSnapshot =
21+
path.join(binDir, 'utils', 'gen_snapshot${executableSuffix}');
22+
final String platformDill =
23+
path.join(sdkDir, 'lib', '_internal', 'vm_platform_strong.dill');
1024

11-
void main(List<String> args) {
12-
Map<String, Command> commands = <String, Command>{};
13-
commands['aot'] = callAOT;
25+
Future<void> generateNative(Kind kind, String sourceFile, String outputFile,
26+
String packages, List<String> defines, bool enableAsserts) async {
27+
final Directory tempDir = Directory.systemTemp.createTempSync();
28+
try {
29+
final String kernelFile = path.join(tempDir.path, 'kernel.dill');
30+
final String snapshotFile = (kind == Kind.aot
31+
? outputFile
32+
: path.join(tempDir.path, 'snapshot.aot'));
1433

15-
// Read -D args that the ArgParser can't handle.
16-
List<String> ds = [];
17-
args = filterDArgs(args, ds);
34+
print('Generating AOT kernel dill.');
35+
final kernelResult = await generateAotKernel(dart, genKernel, platformDill,
36+
sourceFile, kernelFile, packages, defines);
37+
if (kernelResult.exitCode != 0) {
38+
stderr.writeln(kernelResult.stdout);
39+
stderr.writeln(kernelResult.stderr);
40+
await stderr.flush();
41+
throw 'Generating AOT kernel dill failed!';
42+
}
1843

19-
ArgParser parser = ArgParser();
20-
parser.addFlag('help');
21-
ArgParser aotParser = parser.addCommand('aot');
22-
setupAOTArgs(aotParser);
44+
print('Generating AOT snapshot.');
45+
final snapshotResult = await generateAotSnapshot(
46+
genSnapshot, kernelFile, snapshotFile, enableAsserts);
47+
if (snapshotResult.exitCode != 0) {
48+
stderr.writeln(snapshotResult.stdout);
49+
stderr.writeln(snapshotResult.stderr);
50+
await stderr.flush();
51+
throw 'Generating AOT snapshot failed!';
52+
}
2353

24-
ArgResults result = null;
25-
try {
26-
result = parser.parse(args);
27-
} catch (ArgParserException) {
28-
// We handle this case as result == null below.
29-
}
54+
if (kind == Kind.exe) {
55+
print('Generating executable.');
56+
await writeAppendedExecutable(dartaotruntime, snapshotFile, outputFile);
3057

31-
if (result == null || result.command == null || result['help']) {
32-
print('dart2native <command> <args>\n');
33-
print(' command: ');
34-
print(' aot - Compile script into one ahead of time dart snapshot');
35-
return;
36-
}
58+
if (Platform.isLinux || Platform.isMacOS) {
59+
print('Marking binary executable.');
60+
await markExecutable(outputFile);
61+
}
62+
}
3763

38-
if (commands.containsKey(result.command.name)) {
39-
commands[result.command.name](result.command, ds);
40-
return;
64+
print('Generated: ${outputFile}');
65+
} finally {
66+
tempDir.deleteSync(recursive: true);
4167
}
4268
}
4369

44-
void callAOT(ArgResults args, List<String> ds) {
45-
List<String> rest = args.rest;
46-
if (rest.length != 2) {
47-
print(
48-
'Usage: dart2native aot [options] <dart-source-file> <dart-aot-file>\n');
49-
print(
50-
'Dart AOT (ahead-of-time) compile Dart source code into native machine code.');
51-
return;
70+
void printUsage(final ArgParser parser) {
71+
print('Usage: dart2native <dart-script-file> [<options>]');
72+
print('');
73+
print('Generates an executable or an AOT snapshot from <dart-script-file>.');
74+
print('');
75+
print(parser.usage);
76+
}
77+
78+
Future<void> main(List<String> args) async {
79+
final ArgParser parser = ArgParser()
80+
..addMultiOption('define',
81+
abbr: 'D',
82+
valueHelp: 'key=value',
83+
help: 'The values for the environment constants.')
84+
..addFlag('enable-asserts',
85+
negatable: false, help: 'Enable assert statements.')
86+
..addFlag('help',
87+
abbr: 'h', negatable: false, help: 'Displays the help message.')
88+
..addOption('output',
89+
abbr: 'o', valueHelp: 'path', help: 'Path to output file.')
90+
..addOption('output-kind',
91+
abbr: 'k',
92+
allowed: ['exe', 'aot'],
93+
defaultsTo: 'exe',
94+
valueHelp: 'exe|aot',
95+
help:
96+
'Whether to generate a stand-alone executable or an AOT snapshot.')
97+
..addOption('packages',
98+
valueHelp: 'path', help: 'Where to find a package spec file.');
99+
100+
final ArgResults parsedArgs = parser.parse(args);
101+
102+
if (parsedArgs['help']) {
103+
printUsage(parser);
104+
exit(0);
52105
}
53106

54-
aot(rest[0], rest[1], args['build-elf'], args['enable-asserts'], args['tfa'],
55-
args['no-tfa'], args['packages'], ds);
56-
}
107+
if (parsedArgs.rest.length != 1) {
108+
printUsage(parser);
109+
exit(1);
110+
}
57111

58-
List<String> filterDArgs(List<String> args, List<String> ds) {
59-
List<String> result = <String>[];
112+
final Kind kind = {
113+
'aot': Kind.aot,
114+
'exe': Kind.exe,
115+
}[parsedArgs['output-kind']];
60116

61-
args.forEach((String arg) {
62-
if (!arg.startsWith('-D')) {
63-
result.add(arg);
64-
} else {
65-
ds.add(arg);
66-
}
67-
});
117+
final sourcePath = path.canonicalize(path.normalize(parsedArgs.rest[0]));
118+
final outputPath =
119+
path.canonicalize(path.normalize(parsedArgs['output'] != null
120+
? parsedArgs['output']
121+
: {
122+
Kind.aot: '${sourcePath}.aot',
123+
Kind.exe: '${sourcePath}.exe',
124+
}[kind]));
68125

69-
return result;
126+
if (!FileSystemEntity.isFileSync(sourcePath)) {
127+
stderr.writeln(
128+
'"${sourcePath}" is not a file. See \'--help\' for more information.');
129+
await stderr.flush();
130+
exit(1);
131+
}
132+
133+
try {
134+
await generateNative(kind, sourcePath, outputPath, parsedArgs['packages'],
135+
parsedArgs['define'], parsedArgs['enable-asserts']);
136+
} catch (e) {
137+
stderr.writeln('Failed to generate native files:');
138+
stderr.writeln(e);
139+
await stderr.flush();
140+
exit(1);
141+
}
70142
}

pkg/dart2native/lib/dart2native.dart

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
import 'dart:typed_data';
7+
8+
import 'package:path/path.dart' as path;
9+
10+
const appSnapshotPageSize = 4096;
11+
const appjitMagicNumber = <int>[0xdc, 0xdc, 0xf6, 0xf6, 0, 0, 0, 0];
12+
13+
enum Kind { aot, exe }
14+
15+
Future writeAppendedExecutable(
16+
String dartaotruntimePath, String payloadPath, String outputPath) async {
17+
final dartaotruntime = File(dartaotruntimePath);
18+
final int dartaotruntimeLength = dartaotruntime.lengthSync();
19+
20+
final padding =
21+
((appSnapshotPageSize - dartaotruntimeLength) % appSnapshotPageSize);
22+
final padBytes = Uint8List(padding);
23+
final offset = dartaotruntimeLength + padding;
24+
25+
// Note: The offset is always Little Endian regardless of host.
26+
final offsetBytes = new ByteData(8) // 64 bit in bytes.
27+
..setUint64(0, offset, Endian.little);
28+
29+
final outputFile = File(outputPath).openWrite();
30+
outputFile.add(dartaotruntime.readAsBytesSync());
31+
outputFile.add(padBytes);
32+
outputFile.add(File(payloadPath).readAsBytesSync());
33+
outputFile.add(offsetBytes.buffer.asUint8List());
34+
outputFile.add(appjitMagicNumber);
35+
await outputFile.close();
36+
}
37+
38+
Future markExecutable(String outputFile) {
39+
return Process.run('chmod', ['+x', outputFile]);
40+
}
41+
42+
Future generateAotKernel(
43+
String dart,
44+
String genKernel,
45+
String platformDill,
46+
String sourceFile,
47+
String kernelFile,
48+
String packages,
49+
List<String> defines) {
50+
return Process.run(dart, [
51+
genKernel,
52+
'--platform',
53+
platformDill,
54+
'--aot',
55+
'-Ddart.vm.product=true',
56+
...defines,
57+
if (packages != null) ...['--packages', packages],
58+
'-o',
59+
kernelFile,
60+
sourceFile
61+
]);
62+
}
63+
64+
Future generateAotSnapshot(String genSnapshot, String kernelFile,
65+
String snapshotFile, bool enableAsserts) {
66+
return Process.run(genSnapshot, [
67+
'--snapshot-kind=app-aot-blobs',
68+
'--blobs_container_filename=${snapshotFile}',
69+
if (enableAsserts) '--enable-asserts',
70+
kernelFile
71+
]);
72+
}

runtime/bin/snapshot_utils.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,12 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotBlobs(
179179
if (!file->ReadFully(&appended_header, sizeof(appended_header))) {
180180
return nullptr;
181181
}
182+
// Length is always encoded as Little Endian.
183+
const uint64_t appended_length =
184+
Utils::LittleEndianToHost64(appended_header[0]);
182185
if (memcmp(&appended_header[1], appjit_magic_number.bytes,
183186
appjit_magic_number.length) != 0 ||
184-
appended_header[0] <= 0 || !file->SetPosition(appended_header[0])) {
187+
appended_length <= 0 || !file->SetPosition(appended_length)) {
185188
return nullptr;
186189
}
187190

runtime/platform/utils.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,14 @@ class Utils {
299299
static uint32_t HostToLittleEndian32(uint32_t host_value);
300300
static uint64_t HostToLittleEndian64(uint64_t host_value);
301301

302-
static uint32_t BigEndianToHost32(uint32_t be_value) {
303-
// Going between Host <-> BE is the same operation for all practical
304-
// purposes.
302+
// Going between Host <-> LE/BE is the same operation for all practical
303+
// purposes.
304+
static inline uint32_t BigEndianToHost32(uint32_t be_value) {
305305
return HostToBigEndian32(be_value);
306306
}
307+
static inline uint64_t LittleEndianToHost64(uint64_t le_value) {
308+
return HostToLittleEndian64(le_value);
309+
}
307310

308311
static bool DoublesBitEqual(const double a, const double b) {
309312
return bit_cast<int64_t, double>(a) == bit_cast<int64_t, double>(b);

runtime/platform/utils_win.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ inline int Utils::CountTrailingZeros(uword x) {
3838
return static_cast<int>(result);
3939
}
4040

41+
// WARNING: The below functions assume host is always Little Endian!
42+
4143
inline uint16_t Utils::HostToBigEndian16(uint16_t value) {
4244
return _byteswap_ushort(value);
4345
}

0 commit comments

Comments
 (0)