blob: 650a9bf509bfb6f29bdcec20bbadc290422ce419 [file] [log] [blame]
// Copyright (c) 2019, 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:convert';
import 'dart:io';
import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
final isAOTRuntime = path.basenameWithoutExtension(Platform.executable) ==
'dart_precompiled_runtime';
final buildDir = path.dirname(Platform.executable);
final sdkDir = path.dirname(path.dirname(buildDir));
final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
final genKernel = path.join(sdkDir, 'pkg', 'vm', 'tool',
'gen_kernel' + (Platform.isWindows ? '.bat' : ''));
final _genSnapshotBase = 'gen_snapshot' + (Platform.isWindows ? '.exe' : '');
// Slight hack to work around issue that gen_snapshot for simarm_x64 is not
// in the same subdirectory as dart_precompiled_runtime (${MODE}SIMARM), but
// instead it's in ${MODE}SIMARM_X64.
final genSnapshot = File(path.join(buildDir, _genSnapshotBase)).existsSync()
? path.join(buildDir, _genSnapshotBase)
: path.join(buildDir + '_X64', _genSnapshotBase);
final aotRuntime = path.join(
buildDir, 'dart_precompiled_runtime' + (Platform.isWindows ? '.exe' : ''));
String get clangBuildToolsDir {
String archDir;
if (Platform.isLinux) {
archDir = 'linux-x64';
} else if (Platform.isMacOS) {
archDir = 'mac-x64';
} else {
return null;
}
var clangDir = path.join(sdkDir, 'buildtools', archDir, 'clang', 'bin');
return Directory(clangDir).existsSync() ? clangDir : null;
}
Future<void> assembleSnapshot(String assemblyPath, String snapshotPath) async {
if (!Platform.isLinux && !Platform.isMacOS) {
throw "Unsupported platform ${Platform.operatingSystem} for assembling";
}
final ccFlags = <String>[];
final ldFlags = <String>[];
String cc = 'gcc';
String shared = '-shared';
if (Platform.isMacOS) {
cc = 'clang';
} else if (buildDir.endsWith('SIMARM') || buildDir.endsWith('SIMARM64')) {
if (clangBuildToolsDir != null) {
cc = path.join(clangBuildToolsDir, 'clang');
} else {
throw 'Cannot assemble for ${path.basename(buildDir)} '
'without //buildtools on ${Platform.operatingSystem}';
}
}
if (Platform.isMacOS) {
shared = '-dynamiclib';
// Tell Mac linker to give up generating eh_frame from dwarf.
ldFlags.add('-Wl,-no_compact_unwind');
} else if (buildDir.endsWith('SIMARM')) {
ccFlags.add('--target=armv7-linux-gnueabihf');
} else if (buildDir.endsWith('SIMARM64')) {
ccFlags.add('--target=aarch64-linux-gnu');
}
if (buildDir.endsWith('X64') || buildDir.endsWith('SIMARM64')) {
ccFlags.add('-m64');
}
await run(cc, <String>[
...ccFlags,
...ldFlags,
shared,
'-nostdlib',
'-o',
snapshotPath,
assemblyPath,
]);
}
Future<void> stripSnapshot(String snapshotPath, String strippedPath,
{bool forceElf = false}) async {
if (!Platform.isLinux && !Platform.isMacOS) {
throw "Unsupported platform ${Platform.operatingSystem} for stripping";
}
var strip = 'strip';
if ((Platform.isLinux &&
(buildDir.endsWith('SIMARM') || buildDir.endsWith('SIMARM64'))) ||
(Platform.isMacOS && forceElf)) {
if (clangBuildToolsDir != null) {
strip = path.join(clangBuildToolsDir, 'llvm-strip');
} else {
throw 'Cannot strip ELF files for ${path.basename(buildDir)} '
'without //buildtools on ${Platform.operatingSystem}';
}
}
await run(strip, <String>[
'-o',
strippedPath,
snapshotPath,
]);
}
Future<ProcessResult> runHelper(String executable, List<String> args) async {
print('Running $executable ${args.join(' ')}');
final result = await Process.run(executable, args);
print('Subcommand terminated with exit code ${result.exitCode}.');
if (result.stdout.isNotEmpty) {
print('Subcommand stdout:');
print(result.stdout);
}
if (result.exitCode != 0) {
if (result.stderr.isNotEmpty) {
print('Subcommand stderr:');
print(result.stderr);
}
}
return result;
}
Future<bool> testExecutable(String executable) async {
try {
final result = await runHelper(executable, <String>['--version']);
return result.exitCode == 0;
} on ProcessException catch (e) {
print('Got process exception: $e');
return false;
}
}
Future<void> run(String executable, List<String> args) async {
final result = await runHelper(executable, args);
if (result.exitCode != 0) {
throw 'Command failed with unexpected exit code (was ${result.exitCode})';
}
}
Future<List<String>> runOutput(String executable, List<String> args) async {
final result = await runHelper(executable, args);
if (result.exitCode != 0) {
throw 'Command failed with unexpected exit code (was ${result.exitCode})';
}
Expect.isTrue(result.stdout.isNotEmpty);
Expect.isTrue(result.stderr.isEmpty);
return await Stream.value(result.stdout as String)
.transform(const LineSplitter())
.toList();
}
Future<List<String>> runError(String executable, List<String> args) async {
final result = await runHelper(executable, args);
if (result.exitCode == 0) {
throw 'Command did not fail with non-zero exit code';
}
Expect.isTrue(result.stdout.isEmpty);
Expect.isTrue(result.stderr.isNotEmpty);
return await Stream.value(result.stderr as String)
.transform(const LineSplitter())
.toList();
}
const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES';
Future<void> withTempDir(String name, Future<void> fun(String dir)) async {
final tempDir = Directory.systemTemp.createTempSync(name);
try {
await fun(tempDir.path);
} finally {
if (!Platform.environment.containsKey(keepTempKey) ||
Platform.environment[keepTempKey].isEmpty) {
tempDir.deleteSync(recursive: true);
}
}
}